diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index b75511e..1b8ac3c 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -9,26 +9,27 @@ }, { "ImportPath": "github.com/gin-gonic/gin", - "Comment": "v0.4-8-g3b079bb", - "Rev": "3b079bb6f79ae501e73a5401c41b589f6616b61e" + "Comment": "v0.4-16-g28b9ff9", + "Rev": "28b9ff9e3495dabeaea2da86c100effbf1a68346" }, { "ImportPath": "github.com/jessevdk/go-flags", - "Comment": "v1-265-g1c87b97", - "Rev": "1c87b9727cd7c200be13a5d8280d7ea514469235" + "Comment": "v1-285-g1679536", + "Rev": "1679536dcc895411a9f5848d9a0250be7856448c" }, { "ImportPath": "github.com/jmoiron/sqlx", - "Comment": "sqlx-v1.0-49-g79215a1", - "Rev": "79215a1536dd456d2a5afa112479b4f67fbe9f21" + "Comment": "sqlx-v1.0-61-gb468c08", + "Rev": "b468c08552f4efac78b94708eb040170a8184c47" }, { "ImportPath": "github.com/julienschmidt/httprouter", - "Rev": "46807412fe50aaceb73bb57061c2230fd26a1640" + "Rev": "b55664b9e92004aebb7f19a19a9d06271f3a41fc" }, { "ImportPath": "github.com/lib/pq", - "Rev": "7175accbed18058468c07811f76440d6e8d7cf19" + "Comment": "go1.0-cutoff-13-g19eeca3", + "Rev": "19eeca3e30d2577b1761db471ec130810e67f532" }, { "ImportPath": "github.com/mitchellh/go-homedir", @@ -36,7 +37,7 @@ }, { "ImportPath": "github.com/stretchr/testify/assert", - "Rev": "faedd6eb633a4cd77c15f6c9856608701e40d9a2" + "Rev": "2eaa4b48b8954bf871229043b8064ca156a542a5" } ] } diff --git a/Godeps/_workspace/src/github.com/gin-gonic/gin/README.md b/Godeps/_workspace/src/github.com/gin-gonic/gin/README.md index c97164d..94fd949 100644 --- a/Godeps/_workspace/src/github.com/gin-gonic/gin/README.md +++ b/Godeps/_workspace/src/github.com/gin-gonic/gin/README.md @@ -287,6 +287,22 @@ func main() { ``` +####Serving static files + +Use Engine.ServeFiles(path string, root http.FileSystem): + +```go +func main() { + r := gin.Default() + r.Static("/assets", "./assets") + + // Listen and server on 0.0.0.0:8080 + r.Run(":8080") +} +``` + +Note: this will use `httpNotFound` instead of the Router's `NotFound` handler. + ####HTML rendering Using LoadHTMLTemplates() diff --git a/Godeps/_workspace/src/github.com/gin-gonic/gin/binding/binding.go b/Godeps/_workspace/src/github.com/gin-gonic/gin/binding/binding.go index 61a57b1..81ac3fa 100644 --- a/Godeps/_workspace/src/github.com/gin-gonic/gin/binding/binding.go +++ b/Godeps/_workspace/src/github.com/gin-gonic/gin/binding/binding.go @@ -155,7 +155,7 @@ func ensureNotPointer(obj interface{}) { } } -func Validate(obj interface{}) error { +func Validate(obj interface{}, parents ...string) error { typ := reflect.TypeOf(obj) val := reflect.ValueOf(obj) @@ -180,12 +180,19 @@ func Validate(obj interface{}) error { if strings.Index(field.Tag.Get("binding"), "required") > -1 { fieldType := field.Type.Kind() if fieldType == reflect.Struct { - err := Validate(fieldValue) + if reflect.DeepEqual(zero, fieldValue) { + return errors.New("Required " + field.Name) + } + err := Validate(fieldValue, field.Name) if err != nil { return err } } else if reflect.DeepEqual(zero, fieldValue) { - return errors.New("Required " + field.Name) + if len(parents) > 0 { + return errors.New("Required " + field.Name + " on " + parents[0]) + } else { + return errors.New("Required " + field.Name) + } } else if fieldType == reflect.Slice && field.Type.Elem().Kind() == reflect.Struct { err := Validate(fieldValue) if err != nil { diff --git a/Godeps/_workspace/src/github.com/gin-gonic/gin/context.go b/Godeps/_workspace/src/github.com/gin-gonic/gin/context.go index 294d1cc..be25db8 100644 --- a/Godeps/_workspace/src/github.com/gin-gonic/gin/context.go +++ b/Godeps/_workspace/src/github.com/gin-gonic/gin/context.go @@ -186,7 +186,7 @@ func (c *Context) MustGet(key string) interface{} { } /************************************/ -/******** ENCOGING MANAGEMENT********/ +/******** ENCODING MANAGEMENT********/ /************************************/ // This function checks the Content-Type to select a binding engine automatically, diff --git a/Godeps/_workspace/src/github.com/gin-gonic/gin/examples/app-engine/hello.go b/Godeps/_workspace/src/github.com/gin-gonic/gin/examples/app-engine/hello.go index f5daf82..f7d7c84 100644 --- a/Godeps/_workspace/src/github.com/gin-gonic/gin/examples/app-engine/hello.go +++ b/Godeps/_workspace/src/github.com/gin-gonic/gin/examples/app-engine/hello.go @@ -20,4 +20,4 @@ func init() { // Handle all requests using net/http http.Handle("/", r) -} \ No newline at end of file +} diff --git a/Godeps/_workspace/src/github.com/jessevdk/go-flags/completion.go b/Godeps/_workspace/src/github.com/jessevdk/go-flags/completion.go index cb7aed6..d0adfe0 100644 --- a/Godeps/_workspace/src/github.com/jessevdk/go-flags/completion.go +++ b/Godeps/_workspace/src/github.com/jessevdk/go-flags/completion.go @@ -234,7 +234,7 @@ func (c *completion) complete(args []string) []Completion { if opt != nil { // Completion for the argument of 'opt' ret = c.completeValue(opt.value, "", lastarg) - } else if argumentIsOption(lastarg) { + } else if argumentStartsOption(lastarg) { // Complete the option prefix, optname, islong := stripOptionPrefix(lastarg) optname, split, argument := splitOption(prefix, optname, islong) diff --git a/Godeps/_workspace/src/github.com/jessevdk/go-flags/optstyle_other.go b/Godeps/_workspace/src/github.com/jessevdk/go-flags/optstyle_other.go index 794de23..29ca4b6 100644 --- a/Godeps/_workspace/src/github.com/jessevdk/go-flags/optstyle_other.go +++ b/Godeps/_workspace/src/github.com/jessevdk/go-flags/optstyle_other.go @@ -12,10 +12,22 @@ const ( defaultNameArgDelimiter = '=' ) -func argumentIsOption(arg string) bool { +func argumentStartsOption(arg string) bool { return len(arg) > 0 && arg[0] == '-' } +func argumentIsOption(arg string) bool { + if len(arg) > 1 && arg[0] == '-' && arg[1] != '-' { + return true + } + + if len(arg) > 2 && arg[0] == '-' && arg[1] == '-' && arg[2] != '-' { + return true + } + + return false +} + // stripOptionPrefix returns the option without the prefix and whether or // not the option is a long option or not. func stripOptionPrefix(optname string) (prefix string, name string, islong bool) { diff --git a/Godeps/_workspace/src/github.com/jessevdk/go-flags/optstyle_windows.go b/Godeps/_workspace/src/github.com/jessevdk/go-flags/optstyle_windows.go index 096bbff..a51de9c 100644 --- a/Godeps/_workspace/src/github.com/jessevdk/go-flags/optstyle_windows.go +++ b/Godeps/_workspace/src/github.com/jessevdk/go-flags/optstyle_windows.go @@ -12,10 +12,26 @@ const ( defaultNameArgDelimiter = ':' ) +func argumentStartsOption(arg string) bool { + return len(arg) > 0 && (arg[0] == '-' || arg[0] == '/') +} + func argumentIsOption(arg string) bool { // Windows-style options allow front slash for the option // delimiter. - return len(arg) > 0 && (arg[0] == '-' || arg[0] == '/') + if len(arg) > 1 && arg[0] == '/' { + return true + } + + if len(arg) > 1 && arg[0] == '-' && arg[1] != '-' { + return true + } + + if len(arg) > 2 && arg[0] == '-' && arg[1] == '-' && arg[2] != '-' { + return true + } + + return false } // stripOptionPrefix returns the option without the prefix and whether or diff --git a/Godeps/_workspace/src/github.com/jessevdk/go-flags/parser.go b/Godeps/_workspace/src/github.com/jessevdk/go-flags/parser.go index 5a8ec92..6f45a03 100644 --- a/Godeps/_workspace/src/github.com/jessevdk/go-flags/parser.go +++ b/Godeps/_workspace/src/github.com/jessevdk/go-flags/parser.go @@ -24,9 +24,37 @@ type Parser struct { // NamespaceDelimiter separates group namespaces and option long names NamespaceDelimiter string + // UnknownOptionsHandler is a function which gets called when the parser + // encounters an unknown option. The function receives the unknown option + // name, a SplitArgument which specifies its value if set with an argument + // separator, and the remaining command line arguments. + // It should return a new list of remaining arguments to continue parsing, + // or an error to indicate a parse failure. + UnknownOptionHandler func(option string, arg SplitArgument, args []string) ([]string, error) + internalError error } +// SplitArgument represents the argument value of an option that was passed using +// an argument separator. +type SplitArgument interface { + // String returns the option's value as a string, and a boolean indicating + // if the option was present. + Value() (string, bool) +} + +type strArgument struct { + value *string +} + +func (s strArgument) Value() (string, bool) { + if s.value == nil { + return "", false + } + + return *s.value, true +} + // Options provides parser options that change the behavior of the option // parser. type Options uint @@ -204,13 +232,22 @@ func (p *Parser) ParseArgs(args []string) ([]string, error) { ignoreUnknown := (p.Options & IgnoreUnknown) != None parseErr := wrapError(err) - if !(parseErr.Type == ErrUnknownFlag && ignoreUnknown) { + if parseErr.Type != ErrUnknownFlag || (!ignoreUnknown && p.UnknownOptionHandler == nil) { s.err = parseErr break } if ignoreUnknown { s.addArgs(arg) + } else if p.UnknownOptionHandler != nil { + modifiedArgs, err := p.UnknownOptionHandler(optname, strArgument{argument}, s.args) + + if err != nil { + s.err = err + break + } + + s.args = modifiedArgs } } } diff --git a/Godeps/_workspace/src/github.com/jessevdk/go-flags/parser_private.go b/Godeps/_workspace/src/github.com/jessevdk/go-flags/parser_private.go index e25cffd..76be4a7 100644 --- a/Godeps/_workspace/src/github.com/jessevdk/go-flags/parser_private.go +++ b/Godeps/_workspace/src/github.com/jessevdk/go-flags/parser_private.go @@ -170,8 +170,11 @@ func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg arg = *argument } else { arg = s.pop() + if argumentIsOption(arg) { return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got option `%s'", option, arg) + } else if p.Options&PassDoubleDash != 0 && arg == "--" { + return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got double dash `--'", option) } } diff --git a/Godeps/_workspace/src/github.com/jessevdk/go-flags/parser_test.go b/Godeps/_workspace/src/github.com/jessevdk/go-flags/parser_test.go index 30ecb5e..5792872 100644 --- a/Godeps/_workspace/src/github.com/jessevdk/go-flags/parser_test.go +++ b/Godeps/_workspace/src/github.com/jessevdk/go-flags/parser_test.go @@ -1,6 +1,7 @@ package flags import ( + "fmt" "os" "reflect" "strconv" @@ -312,6 +313,7 @@ func TestOptionAsArgument(t *testing.T) { expectError bool errType ErrorType errMsg string + rest []string }{ { // short option must not be accepted as argument @@ -327,10 +329,25 @@ func TestOptionAsArgument(t *testing.T) { errType: ErrExpectedArgument, errMsg: "expected argument for flag `--string-slice', but got option `--other-option'", }, + { + // long option must not be accepted as argument + args: []string{"--string-slice", "--"}, + expectError: true, + errType: ErrExpectedArgument, + errMsg: "expected argument for flag `--string-slice', but got double dash `--'", + }, { // quoted and appended option should be accepted as argument (even if it looks like an option) args: []string{"--string-slice", "foobar", "--string-slice=\"--other-option\""}, }, + { + // Accept any single character arguments including '-' + args: []string{"--string-slice", "-"}, + }, + { + args: []string{"-o", "-", "-"}, + rest: []string{"-", "-"}, + }, } var opts struct { StringSlice []string `long:"string-slice"` @@ -341,7 +358,74 @@ func TestOptionAsArgument(t *testing.T) { if test.expectError { assertParseFail(t, test.errType, test.errMsg, &opts, test.args...) } else { - assertParseSuccess(t, &opts, test.args...) + args := assertParseSuccess(t, &opts, test.args...) + + assertStringArray(t, args, test.rest) } } } + +func TestUnknownFlagHandler(t *testing.T) { + + var opts struct { + Flag1 string `long:"flag1"` + Flag2 string `long:"flag2"` + } + + p := NewParser(&opts, None) + + var unknownFlag1 string + var unknownFlag2 bool + var unknownFlag3 string + + // Set up a callback to intercept unknown options during parsing + p.UnknownOptionHandler = func(option string, arg SplitArgument, args []string) ([]string, error) { + if option == "unknownFlag1" { + if argValue, ok := arg.Value(); ok { + unknownFlag1 = argValue + return args, nil + } + // consume a value from remaining args list + unknownFlag1 = args[0] + return args[1:], nil + } else if option == "unknownFlag2" { + // treat this one as a bool switch, don't consume any args + unknownFlag2 = true + return args, nil + } else if option == "unknownFlag3" { + if argValue, ok := arg.Value(); ok { + unknownFlag3 = argValue + return args, nil + } + // consume a value from remaining args list + unknownFlag3 = args[0] + return args[1:], nil + } + + return args, fmt.Errorf("Unknown flag: %v", option) + } + + // Parse args containing some unknown flags, verify that + // our callback can handle all of them + _, err := p.ParseArgs([]string{"--flag1=stuff", "--unknownFlag1", "blah", "--unknownFlag2", "--unknownFlag3=baz", "--flag2=foo"}) + + if err != nil { + assertErrorf(t, "Parser returned unexpected error %v", err) + } + + assertString(t, opts.Flag1, "stuff") + assertString(t, opts.Flag2, "foo") + assertString(t, unknownFlag1, "blah") + assertString(t, unknownFlag3, "baz") + + if !unknownFlag2 { + assertErrorf(t, "Flag should have been set by unknown handler, but had value: %v", unknownFlag2) + } + + // Parse args with unknown flags that callback doesn't handle, verify it returns error + _, err = p.ParseArgs([]string{"--flag1=stuff", "--unknownFlagX", "blah", "--flag2=foo"}) + + if err == nil { + assertErrorf(t, "Parser should have returned error, but returned nil") + } +} diff --git a/Godeps/_workspace/src/github.com/jmoiron/sqlx/README.md b/Godeps/_workspace/src/github.com/jmoiron/sqlx/README.md index e59340a..4e3eb6d 100644 --- a/Godeps/_workspace/src/github.com/jmoiron/sqlx/README.md +++ b/Godeps/_workspace/src/github.com/jmoiron/sqlx/README.md @@ -141,12 +141,12 @@ func main() { // if you have null fields and use SELECT *, you must use sql.Null* in your struct places := []Place{} - err := db.Select(&places, "SELECT * FROM place ORDER BY telcode ASC") + err = db.Select(&places, "SELECT * FROM place ORDER BY telcode ASC") if err != nil { - fmt.Printf(err) + fmt.Println(err) return } - usa, singsing, honkers = places[0], places[1], places[2] + usa, singsing, honkers := places[0], places[1], places[2] fmt.Printf("%#v\n%#v\n%#v\n", usa, singsing, honkers) // Place{Country:"United States", City:sql.NullString{String:"New York", Valid:true}, TelCode:1} @@ -159,7 +159,7 @@ func main() { for rows.Next() { err := rows.StructScan(&place) if err != nil { - log.Fataln(err) + log.Fatalln(err) } fmt.Printf("%#v\n", place) } @@ -177,12 +177,12 @@ func main() { }) // Selects Mr. Smith from the database - rows, err := db.NamedQuery(`SELECT * FROM person WHERE first_name=:fn`, map[string]interface{}{"fn": "Bin"}) + rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:fn`, map[string]interface{}{"fn": "Bin"}) // Named queries can also use structs. Their bind names follow the same rules // as the name -> db mapping, so struct fields are lowercased and the `db` tag // is taken into consideration. - rows, err := db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason) + rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason) } ``` diff --git a/Godeps/_workspace/src/github.com/jmoiron/sqlx/bind.go b/Godeps/_workspace/src/github.com/jmoiron/sqlx/bind.go index ff65a50..2f1ec2b 100644 --- a/Godeps/_workspace/src/github.com/jmoiron/sqlx/bind.go +++ b/Godeps/_workspace/src/github.com/jmoiron/sqlx/bind.go @@ -16,11 +16,11 @@ const ( // BindType returns the bindtype for a given database given a drivername. func BindType(driverName string) int { switch driverName { - case "postgres": + case "postgres", "pgx": return DOLLAR case "mysql": return QUESTION - case "sqlite": + case "sqlite3": return QUESTION case "oci8": return NAMED diff --git a/Godeps/_workspace/src/github.com/jmoiron/sqlx/named.go b/Godeps/_workspace/src/github.com/jmoiron/sqlx/named.go index 329bd3e..d753518 100644 --- a/Godeps/_workspace/src/github.com/jmoiron/sqlx/named.go +++ b/Godeps/_workspace/src/github.com/jmoiron/sqlx/named.go @@ -151,7 +151,6 @@ func bindArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{ fields := m.TraversalsByName(v.Type(), names) for i, t := range fields { if len(t) == 0 { - fmt.Println(fields, names) return arglist, fmt.Errorf("could not find name %s in %#v", names[i], arg) } val := reflectx.FieldByIndexesReadOnly(v, t) diff --git a/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/reflect.go b/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/reflect.go index 847b760..4699858 100644 --- a/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/reflect.go +++ b/Godeps/_workspace/src/github.com/jmoiron/sqlx/reflectx/reflect.go @@ -137,6 +137,9 @@ func FieldByIndexes(v reflect.Value, indexes []int) reflect.Value { alloc := reflect.New(Deref(v.Type())) v.Set(alloc) } + if v.Kind() == reflect.Map && v.IsNil() { + v.Set(reflect.MakeMap(v.Type())) + } } return v } diff --git a/Godeps/_workspace/src/github.com/jmoiron/sqlx/sqlx_test.go b/Godeps/_workspace/src/github.com/jmoiron/sqlx/sqlx_test.go index bc13485..37babf1 100644 --- a/Godeps/_workspace/src/github.com/jmoiron/sqlx/sqlx_test.go +++ b/Godeps/_workspace/src/github.com/jmoiron/sqlx/sqlx_test.go @@ -12,9 +12,12 @@ package sqlx import ( "database/sql" + "database/sql/driver" + "encoding/json" "fmt" "log" "os" + "reflect" "strings" "testing" "time" @@ -1124,6 +1127,75 @@ func TestBindMap(t *testing.T) { } } +// Test for #117, embedded nil maps + +type Message struct { + Text string `db:"string"` + Properties PropertyMap // Stored as JSON in the database +} +type PropertyMap map[string]string + +// Implement driver.Valuer and sql.Scanner interfaces on PropertyMap +func (p PropertyMap) Value() (driver.Value, error) { + if len(p) == 0 { + return nil, nil + } + return json.Marshal(p) +} + +func (p PropertyMap) Scan(src interface{}) error { + v := reflect.ValueOf(src) + if !v.IsValid() || v.IsNil() { + return nil + } + if data, ok := src.([]byte); ok { + return json.Unmarshal(data, &p) + } + return fmt.Errorf("Could not not decode type %T -> %T", src, p) +} + +func TestEmbeddedMaps(t *testing.T) { + var schema = Schema{ + create: ` + CREATE TABLE message ( + string text, + properties text + );`, + drop: `drop table message;`, + } + + RunWithSchema(schema, t, func(db *DB, t *testing.T) { + messages := []Message{ + {"Hello, World", PropertyMap{"one": "1", "two": "2"}}, + {"Thanks, Joy", PropertyMap{"pull": "request"}}, + } + q1 := `INSERT INTO message (string, properties) VALUES (:string, :properties)` + for _, m := range messages { + _, err := db.NamedExec(q1, m) + if err != nil { + t.Error(err) + } + } + var count int + err := db.Get(&count, "SELECT count(*) FROM message") + if err != nil { + t.Error(err) + } + if count != len(messages) { + t.Errorf("Expected %d messages in DB, found %d", len(messages), count) + } + + var m Message + err = db.Get(&m, "SELECT * FROM message LIMIT 1") + if err != nil { + t.Error(err) + } + if m.Properties == nil { + t.Error("Expected m.Properties to not be nil, but it was.") + } + }) +} + func TestBindStruct(t *testing.T) { var err error diff --git a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/.travis.yml b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/.travis.yml index b0cf782..814710d 100644 --- a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/.travis.yml +++ b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/.travis.yml @@ -3,4 +3,5 @@ go: - 1.1 - 1.2 - 1.3 + - 1.4 - tip diff --git a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/README.md b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/README.md index 082e4cf..e06c1be 100644 --- a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/README.md +++ b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/README.md @@ -189,8 +189,9 @@ for example the [Gorilla handlers](http://www.gorillatoolkit.org/pkg/handlers). Or you could [just write your own](http://justinas.org/writing-http-middleware-in-go/), it's very easy! -Alternatively, you could try [a framework building upon HttpRouter](#web-frameworks-building-upon-httprouter). +Alternatively, you could try [a framework building upon HttpRouter](#web-frameworks--co-based-on-httprouter). +### Multi-domain / Sub-domains Here is a quick example: Does your server serve multiple domains / hosts? You want to use sub-domains? Define a router per host! @@ -228,7 +229,69 @@ func main() { } ``` -## Web Frameworks building upon HttpRouter +### Basic Authentication +Another quick example: Basic Authentification (RFC 2617) for handles: + +```go +package main + +import ( + "bytes" + "encoding/base64" + "fmt" + "github.com/julienschmidt/httprouter" + "net/http" + "log" + "strings" +) + +func BasicAuth(h httprouter.Handle, user, pass []byte) httprouter.Handle { + return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + const basicAuthPrefix string = "Basic " + + // Get the Basic Authentication credentials + auth := r.Header.Get("Authorization") + if strings.HasPrefix(auth, basicAuthPrefix) { + // Check credentials + payload, err := base64.StdEncoding.DecodeString(auth[len(basicAuthPrefix):]) + if err == nil { + pair := bytes.SplitN(payload, []byte(":"), 2) + if len(pair) == 2 && bytes.Equal(pair[0], user) && bytes.Equal(pair[1], pass) { + // Delegate request to the given handle + h(w, r, ps) + return + } + } + } + + // Request Basic Authentication otherwise + w.Header().Set("WWW-Authenticate", "Basic realm=Restricted") + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + } +} + +func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + fmt.Fprint(w, "Not protected!\n") +} + +func Protected(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + fmt.Fprint(w, "Protected!\n") +} + +func main() { + user := []byte("gordon") + pass := []byte("secret!") + + router := httprouter.New() + router.GET("/", Index) + router.GET("/protected/", BasicAuth(Protected, user, pass)) + + log.Fatal(http.ListenAndServe(":8080", router)) +} +``` + +## Web Frameworks & Co based on HttpRouter If the HttpRouter is a bit too minimalistic for you, you might try one of the following more high-level 3rd-party web frameworks building upon the HttpRouter package: * [Gin](https://github.com/gin-gonic/gin): Features a martini-like API with much better performance * [Hikaru](https://github.com/najeira/hikaru): Supports standalone and Google AppEngine +* [Hitch](https://github.com/nbio/hitch): Hitch ties httprouter, [httpcontext](https://github.com/nbio/httpcontext), and middleware up in a bow diff --git a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router.go b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router.go index 3f3d163..7716dc7 100644 --- a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router.go +++ b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router.go @@ -159,6 +159,11 @@ func (r *Router) GET(path string, handle Handle) { r.Handle("GET", path, handle) } +// HEAD is a shortcut for router.Handle("HEAD", path, handle) +func (r *Router) HEAD(path string, handle Handle) { + r.Handle("HEAD", path, handle) +} + // POST is a shortcut for router.Handle("POST", path, handle) func (r *Router) POST(path string, handle Handle) { r.Handle("POST", path, handle) @@ -284,7 +289,7 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { } if tsr && r.RedirectTrailingSlash { - if path[len(path)-1] == '/' { + if len(path) > 1 && path[len(path)-1] == '/' { req.URL.Path = path[:len(path)-1] } else { req.URL.Path = path + "/" diff --git a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router_test.go b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router_test.go index ca59066..7c8ac09 100644 --- a/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router_test.go +++ b/Godeps/_workspace/src/github.com/julienschmidt/httprouter/router_test.go @@ -76,7 +76,7 @@ func (h handlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) { } func TestRouterAPI(t *testing.T) { - var get, post, put, patch, delete, handler, handlerFunc bool + var get, head, post, put, patch, delete, handler, handlerFunc bool httpHandler := handlerStruct{&handler} @@ -84,6 +84,9 @@ func TestRouterAPI(t *testing.T) { router.GET("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) { get = true }) + router.HEAD("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) { + head = true + }) router.POST("/POST", func(w http.ResponseWriter, r *http.Request, _ Params) { post = true }) @@ -109,6 +112,12 @@ func TestRouterAPI(t *testing.T) { t.Error("routing GET failed") } + r, _ = http.NewRequest("HEAD", "/GET", nil) + router.ServeHTTP(w, r) + if !head { + t.Error("routing HEAD failed") + } + r, _ = http.NewRequest("POST", "/POST", nil) router.ServeHTTP(w, r) if !post { @@ -162,6 +171,7 @@ func TestRouterNotFound(t *testing.T) { router := New() router.GET("/path", handlerFunc) router.GET("/dir/", handlerFunc) + router.GET("/", handlerFunc) testRoutes := []struct { route string @@ -170,6 +180,7 @@ func TestRouterNotFound(t *testing.T) { }{ {"/path/", 301, "map[Location:[/path]]"}, // TSR -/ {"/dir", 301, "map[Location:[/dir/]]"}, // TSR +/ + {"", 301, "map[Location:[/]]"}, // TSR +/ {"/PATH", 301, "map[Location:[/path]]"}, // Fixed Case {"/DIR/", 301, "map[Location:[/dir/]]"}, // Fixed Case {"/PATH/", 301, "map[Location:[/path]]"}, // Fixed Case -/ diff --git a/Godeps/_workspace/src/github.com/lib/pq/.travis.yml b/Godeps/_workspace/src/github.com/lib/pq/.travis.yml index 55eda79..82df2a0 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/.travis.yml +++ b/Godeps/_workspace/src/github.com/lib/pq/.travis.yml @@ -1,11 +1,10 @@ language: go go: - - 1.0.2 - - 1.0.3 - 1.1 - 1.2 - 1.3 + - 1.4 - tip before_install: @@ -46,6 +45,7 @@ env: - PQGOSSLTESTS=1 - PQSSLCERTTEST_PATH=$PWD/certs matrix: + - PGVERSION=9.4 - PGVERSION=9.3 - PGVERSION=9.2 - PGVERSION=9.1 diff --git a/Godeps/_workspace/src/github.com/lib/pq/README.md b/Godeps/_workspace/src/github.com/lib/pq/README.md index fb385a8..cb15d6f 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/README.md +++ b/Godeps/_workspace/src/github.com/lib/pq/README.md @@ -58,6 +58,7 @@ code still exists in here. * Charlie Melbye (cmelbye) * Chris Bandy (cbandy) * Chris Walsh (cwds) +* Dan Sosedoff (sosedoff) * Daniel Farina (fdr) * Eric Chlebek (echlebek) * Everyone at The Go Team @@ -72,6 +73,7 @@ code still exists in here. * Jeremy Jay (pbnjay) * Joakim Sernbrant (serbaut) * John Gallagher (jgallagher) +* Jonathan Rudenberg (titanous) * Joël Stemmer (jstemmer) * Kamil Kisiel (kisielk) * Kelly Dunn (kellydunn) @@ -92,4 +94,5 @@ code still exists in here. * Ryan Smith (ryandotsmith) * Samuel Stauffer (samuel) * Timothée Peignier (cyberdelia) +* TruongSinh Tran-Nguyen (truongsinh) * notedit (notedit) diff --git a/Godeps/_workspace/src/github.com/lib/pq/bench_test.go b/Godeps/_workspace/src/github.com/lib/pq/bench_test.go index 2b8ec42..f2eb1da 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/bench_test.go +++ b/Godeps/_workspace/src/github.com/lib/pq/bench_test.go @@ -35,7 +35,6 @@ func BenchmarkSelectSeries(b *testing.B) { } func benchQuery(b *testing.B, query string, result interface{}) { - b.Skip("current pq database-backed benchmarks are inconsistent") b.StopTimer() db := openTestConn(b) defer db.Close() @@ -183,7 +182,6 @@ func BenchmarkPreparedSelectSeries(b *testing.B) { } func benchPreparedQuery(b *testing.B, query string, result interface{}) { - b.Skip("current pq database-backed benchmarks are inconsistent") b.StopTimer() db := openTestConn(b) defer db.Close() diff --git a/Godeps/_workspace/src/github.com/lib/pq/conn.go b/Godeps/_workspace/src/github.com/lib/pq/conn.go index 8006497..44e4833 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/conn.go +++ b/Godeps/_workspace/src/github.com/lib/pq/conn.go @@ -30,6 +30,7 @@ var ( ErrInFailedTransaction = errors.New("pq: Could not complete operation in a failed transaction") ErrSSLNotSupported = errors.New("pq: SSL is not enabled on the server") ErrSSLKeyHasWorldPermissions = errors.New("pq: Private key file has group or world access. Permissions should be u=rw (0600) or less.") + ErrCouldNotDetectUsername = errors.New("pq: Could not detect default username. Please provide one explicitly.") ) type drv struct{} @@ -71,6 +72,7 @@ func (s transactionStatus) String() string { default: errorf("unknown transactionStatus %d", s) } + panic("not reached") } @@ -490,7 +492,6 @@ func (cn *conn) simpleExec(q string) (res driver.Result, commandTag string, err errorf("unknown response for simple query: %q", t) } } - panic("not reached") } func (cn *conn) simpleQuery(q string) (res driver.Rows, err error) { @@ -543,7 +544,6 @@ func (cn *conn) simpleQuery(q string) (res driver.Rows, err error) { errorf("unknown response for simple query: %q", t) } } - panic("not reached") } func (cn *conn) prepareTo(q, stmtName string) (_ *stmt, err error) { @@ -587,8 +587,6 @@ func (cn *conn) prepareTo(q, stmtName string) (_ *stmt, err error) { errorf("unexpected describe rows response: %q", t) } } - - panic("not reached") } func (cn *conn) Prepare(q string) (_ driver.Stmt, err error) { @@ -766,8 +764,6 @@ func (cn *conn) recv() (t byte, r *readBuf) { return } } - - panic("not reached") } // recv1Buf is exactly equivalent to recv1, except it uses a buffer supplied by @@ -788,8 +784,6 @@ func (cn *conn) recv1Buf(r *readBuf) byte { return t } } - - panic("not reached") } // recv1 receives a message from the backend, panicking if an error occurs @@ -854,9 +848,9 @@ func (cn *conn) verifyCA(client *tls.Conn, tlsConf *tls.Config) { } certs := client.ConnectionState().PeerCertificates opts := x509.VerifyOptions{ - DNSName: client.ConnectionState().ServerName, + DNSName: client.ConnectionState().ServerName, Intermediates: x509.NewCertPool(), - Roots: tlsConf.RootCAs, + Roots: tlsConf.RootCAs, } for i, cert := range certs { if i == 0 { @@ -870,7 +864,6 @@ func (cn *conn) verifyCA(client *tls.Conn, tlsConf *tls.Config) { } } - // This function sets up SSL client certificates based on either the "sslkey" // and "sslcert" settings (possibly set via the environment variables PGSSLKEY // and PGSSLCERT, respectively), or if they aren't set, from the .postgresql @@ -884,7 +877,7 @@ func (cn *conn) setupSSLClientCertificates(tlsConf *tls.Config, o values) { sslkey := o.Get("sslkey") sslcert := o.Get("sslcert") if sslkey != "" && sslcert != "" { - // If the user has set a sslkey and sslcert, they *must* exist. + // If the user has set an sslkey and sslcert, they *must* exist. missingOk = false } else { // Automatically load certificates from ~/.postgresql. @@ -901,7 +894,7 @@ func (cn *conn) setupSSLClientCertificates(tlsConf *tls.Config, o values) { missingOk = true } - // Check that both files exist, and report the error or stop depending on + // Check that both files exist, and report the error or stop, depending on // which behaviour we want. Note that we don't do any more extensive // checks than this (such as checking that the paths aren't directories); // LoadX509KeyPair() will take care of the rest. @@ -948,7 +941,7 @@ func (cn *conn) setupSSLCA(tlsConf *tls.Config, o values) { } } -// isDriverSetting returns true iff a setting is purely for the configuring the +// isDriverSetting returns true iff a setting is purely for configuring the // driver's options and should not be sent to the server in the connection // startup packet. func isDriverSetting(key string) bool { @@ -967,7 +960,6 @@ func isDriverSetting(key string) bool { default: return false } - panic("not reached") } func (cn *conn) startup(o values) { @@ -1124,8 +1116,6 @@ func (st *stmt) Exec(v []driver.Value) (res driver.Result, err error) { errorf("unknown exec response: %q", t) } } - - panic("not reached") } func (st *stmt) exec(v []driver.Value) { @@ -1287,7 +1277,6 @@ func (rs *rows) Close() error { return err } } - panic("not reached") } func (rs *rows) Columns() []string { @@ -1337,8 +1326,6 @@ func (rs *rows) Next(dest []driver.Value) (err error) { errorf("unexpected message after execute: %q", t) } } - - panic("not reached") } // QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be diff --git a/Godeps/_workspace/src/github.com/lib/pq/conn_test.go b/Godeps/_workspace/src/github.com/lib/pq/conn_test.go index 378e110..6c3c6b5 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/conn_test.go +++ b/Godeps/_workspace/src/github.com/lib/pq/conn_test.go @@ -7,8 +7,6 @@ import ( "io" "os" "reflect" - "runtime" - "strings" "testing" "time" ) @@ -56,12 +54,6 @@ func getServerVersion(t *testing.T, db *sql.DB) int { } func TestReconnect(t *testing.T) { - if runtime.Version() == "go1.0.2" { - fmt.Println("Skipping failing test; " + - "fixed in database/sql on go1.0.3+") - return - } - db1 := openTestConn(t) defer db1.Close() tx, err := db1.Begin() @@ -838,11 +830,6 @@ func TestIssue196(t *testing.T) { // Test that any CommandComplete messages sent before the query results are // ignored. func TestIssue282(t *testing.T) { - if strings.HasPrefix(runtime.Version(), "go1.0") { - fmt.Println("Skipping test due to lack of Queryer implementation") - return - } - db := openTestConn(t) defer db.Close() @@ -878,6 +865,60 @@ func TestReadFloatPrecision(t *testing.T) { } } +func TestXactMultiStmt(t *testing.T) { + // minified test case based on bug reports from + // pico303@gmail.com and rangelspam@gmail.com + t.Skip("Skipping failing test") + db := openTestConn(t) + defer db.Close() + + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer tx.Commit() + + rows, err := tx.Query("select 1") + if err != nil { + t.Fatal(err) + } + + if rows.Next() { + var val int32 + if err = rows.Scan(&val); err != nil { + t.Fatal(err) + } + } else { + t.Fatal("Expected at least one row in first query in xact") + } + + rows2, err := tx.Query("select 2") + if err != nil { + t.Fatal(err) + } + + if rows2.Next() { + var val2 int32 + if err := rows2.Scan(&val2); err != nil { + t.Fatal(err) + } + } else { + t.Fatal("Expected at least one row in second query in xact") + } + + if err = rows.Err(); err != nil { + t.Fatal(err) + } + + if err = rows2.Err(); err != nil { + t.Fatal(err) + } + + if err = tx.Commit(); err != nil { + t.Fatal(err) + } +} + var envParseTests = []struct { Expected map[string]string Env []string @@ -1168,14 +1209,15 @@ func TestRuntimeParameters(t *testing.T) { if err != nil { t.Fatal(err) } - defer db.Close() // application_name didn't exist before 9.0 if test.param == "application_name" && getServerVersion(t, db) < 90000 { + db.Close() continue } tryGetParameterValue := func() (value string, outcome RuntimeTestResult) { + defer db.Close() row := db.QueryRow("SELECT current_setting($1)", test.param) err = row.Scan(&value) if err != nil { diff --git a/Godeps/_workspace/src/github.com/lib/pq/conn_xact_test.go b/Godeps/_workspace/src/github.com/lib/pq/conn_xact_test.go deleted file mode 100644 index 4c635d4..0000000 --- a/Godeps/_workspace/src/github.com/lib/pq/conn_xact_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// +build go1.1 - -package pq - -import ( - "testing" -) - -func TestXactMultiStmt(t *testing.T) { - // minified test case based on bug reports from - // pico303@gmail.com and rangelspam@gmail.com - t.Skip("Skipping failing test") - db := openTestConn(t) - defer db.Close() - - tx, err := db.Begin() - if err != nil { - t.Fatal(err) - } - defer tx.Commit() - - rows, err := tx.Query("select 1") - if err != nil { - t.Fatal(err) - } - - if rows.Next() { - var val int32 - if err = rows.Scan(&val); err != nil { - t.Fatal(err) - } - } else { - t.Fatal("Expected at least one row in first query in xact") - } - - rows2, err := tx.Query("select 2") - if err != nil { - t.Fatal(err) - } - - if rows2.Next() { - var val2 int32 - if err := rows2.Scan(&val2); err != nil { - t.Fatal(err) - } - } else { - t.Fatal("Expected at least one row in second query in xact") - } - - if err = rows.Err(); err != nil { - t.Fatal(err) - } - - if err = rows2.Err(); err != nil { - t.Fatal(err) - } - - if err = tx.Commit(); err != nil { - t.Fatal(err) - } -} diff --git a/Godeps/_workspace/src/github.com/lib/pq/copy.go b/Godeps/_workspace/src/github.com/lib/pq/copy.go index 472e835..18c04e7 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/copy.go +++ b/Godeps/_workspace/src/github.com/lib/pq/copy.go @@ -4,7 +4,8 @@ import ( "database/sql/driver" "encoding/binary" "errors" - "sync/atomic" + "fmt" + "sync" ) var ( @@ -48,9 +49,10 @@ type copyin struct { rowData chan []byte done chan bool - closed bool - err error - errorset int32 + closed bool + + sync.Mutex // guards err + err error } const ciBufferSize = 64 * 1024 @@ -67,7 +69,7 @@ func (cn *conn) prepareCopyIn(q string) (_ driver.Stmt, err error) { cn: cn, buffer: make([]byte, 0, ciBufferSize), rowData: make(chan []byte), - done: make(chan bool), + done: make(chan bool, 1), } // add CopyData identifier + 4 bytes for message length ci.buffer = append(ci.buffer, 'd', 0, 0, 0, 0) @@ -123,8 +125,6 @@ awaitCopyInResponse: errorf("unknown response for CopyFail: %q", t) } } - - panic("not reached") } func (ci *copyin) flush(buf []byte) { @@ -139,31 +139,48 @@ func (ci *copyin) flush(buf []byte) { func (ci *copyin) resploop() { for { - t, r := ci.cn.recv1() + var r readBuf + t, err := ci.cn.recvMessage(&r) + if err != nil { + ci.cn.bad = true + ci.setError(err) + ci.done <- true + return + } switch t { case 'C': // complete case 'Z': - ci.cn.processReadyForQuery(r) + ci.cn.processReadyForQuery(&r) ci.done <- true return case 'E': - err := parseError(r) + err := parseError(&r) ci.setError(err) default: ci.cn.bad = true - errorf("unknown response: %q", t) + ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t)) + ci.done <- true + return } } } func (ci *copyin) isErrorSet() bool { - return atomic.LoadInt32(&ci.errorset) != 0 + ci.Lock() + isSet := (ci.err != nil) + ci.Unlock() + return isSet } +// setError() sets ci.err if one has not been set already. Caller must not be +// holding ci.Mutex. func (ci *copyin) setError(err error) { - ci.err = err - atomic.StoreInt32(&ci.errorset, 1) + ci.Lock() + if ci.err == nil { + ci.err = err + } + ci.Unlock() } func (ci *copyin) NumInput() int { diff --git a/Godeps/_workspace/src/github.com/lib/pq/copy_test.go b/Godeps/_workspace/src/github.com/lib/pq/copy_test.go index fbd5a11..e489f22 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/copy_test.go +++ b/Godeps/_workspace/src/github.com/lib/pq/copy_test.go @@ -275,6 +275,62 @@ func TestCopySyntaxError(t *testing.T) { } } +// Tests for connection errors in copyin.resploop() +func TestCopyRespLoopConnectionError(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + var pid int + err = txn.QueryRow("SELECT pg_backend_pid()").Scan(&pid) + if err != nil { + t.Fatal(err) + } + + _, err = txn.Exec("CREATE TEMP TABLE temp (a int)") + if err != nil { + t.Fatal(err) + } + + stmt, err := txn.Prepare(CopyIn("temp", "a")) + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("SELECT pg_terminate_backend($1)", pid) + if err != nil { + t.Fatal(err) + } + + // We have to try and send something over, since postgres won't process + // SIGTERMs while it's waiting for CopyData/CopyEnd messages; see + // tcop/postgres.c. + _, err = stmt.Exec(1) + if err != nil { + t.Fatal(err) + } + _, err = stmt.Exec() + if err == nil { + t.Fatalf("expected error") + } + pge, ok := err.(*Error) + if !ok { + t.Fatalf("expected *pq.Error, got %+#v", err) + } else if pge.Code.Name() != "admin_shutdown" { + t.Fatalf("expected admin_shutdown, got %s", pge.Code.Name()) + } + + err = stmt.Close() + if err != nil { + t.Fatal(err) + } +} + func BenchmarkCopyIn(b *testing.B) { db := openTestConn(b) defer db.Close() diff --git a/Godeps/_workspace/src/github.com/lib/pq/encode.go b/Godeps/_workspace/src/github.com/lib/pq/encode.go index e70f8b8..3887f72 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/encode.go +++ b/Godeps/_workspace/src/github.com/lib/pq/encode.go @@ -218,6 +218,8 @@ func (c *locationCache) getLocation(offset int) *time.Location { // time.Parse and the Postgres date formatting quirks. func parseTs(currentLocation *time.Location, str string) (result time.Time) { monSep := strings.IndexRune(str, '-') + // this is Gregorian year, not ISO Year + // In Gregorian system, the year 1 BC is followed by AD 1 year := mustAtoi(str[:monSep]) daySep := monSep + 3 month := mustAtoi(str[monSep+1 : daySep]) @@ -245,7 +247,6 @@ func parseTs(currentLocation *time.Location, str string) (result time.Time) { nanoSec := 0 tzOff := 0 - bcSign := 1 if remainderIdx < len(str) && str[remainderIdx:remainderIdx+1] == "." { fracStart := remainderIdx + 1 @@ -281,14 +282,17 @@ func parseTs(currentLocation *time.Location, str string) (result time.Time) { } tzOff = tzSign * ((tzHours * 60 * 60) + (tzMin * 60) + tzSec) } + var isoYear int if remainderIdx < len(str) && str[remainderIdx:remainderIdx+3] == " BC" { - bcSign = -1 + isoYear = 1 - year remainderIdx += 3 + } else { + isoYear = year } if remainderIdx < len(str) { errorf("expected end of input, got %v", str[remainderIdx:]) } - t := time.Date(bcSign*year, time.Month(month), day, + t := time.Date(isoYear, time.Month(month), day, hour, minute, second, nanoSec, globalLocationCache.getLocation(tzOff)) @@ -312,8 +316,16 @@ func formatTs(t time.Time) (b []byte) { b = []byte(t.Format(time.RFC3339Nano)) // Need to send dates before 0001 A.D. with " BC" suffix, instead of the // minus sign preferred by Go. - if b[0] == '-' { - b = append(b[1:], ' ', 'B', 'C') + // Beware, "0000" in ISO is "1 BC", "-0001" is "2 BC" and so on + bc := false + if t.Year() <= 0 { + // flip year sign, and add 1, e.g: "0" will be "1", and "-10" will be "11" + t = t.AddDate((-t.Year())*2+1, 0, 0) + bc = true + } + b = []byte(t.Format(time.RFC3339Nano)) + if bc { + b = append(b, " BC"...) } _, offset := t.Zone() diff --git a/Godeps/_workspace/src/github.com/lib/pq/encode_test.go b/Godeps/_workspace/src/github.com/lib/pq/encode_test.go index 262fccc..e835f2b 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/encode_test.go +++ b/Godeps/_workspace/src/github.com/lib/pq/encode_test.go @@ -30,8 +30,8 @@ func TestScanNilTimestamp(t *testing.T) { } var timeTests = []struct { - str string - timeval time.Time + str string + timeval time.Time }{ {"22001-02-03", time.Date(22001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))}, {"2001-02-03", time.Date(2001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))}, @@ -57,11 +57,17 @@ var timeTests = []struct { time.FixedZone("", -(7*60*60+30*60+9)))}, {"2001-02-03 04:05:06+07", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 7*60*60))}, - //{"10000-02-03 04:05:06 BC", time.Date(-10000, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))}, - {"0010-02-03 04:05:06 BC", time.Date(-10, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))}, - {"0010-02-03 04:05:06.123 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, - {"0010-02-03 04:05:06.123-07 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, + {"0011-02-03 04:05:06 BC", time.Date(-10, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))}, + {"0011-02-03 04:05:06.123 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"0011-02-03 04:05:06.123-07 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", -7*60*60))}, + {"0001-02-03 04:05:06.123", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"0001-02-03 04:05:06.123 BC", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)}, + {"0001-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"0002-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)}, + {"0002-02-03 04:05:06.123 BC", time.Date(-1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"12345-02-03 04:05:06.1", time.Date(12345, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))}, + {"123456-02-03 04:05:06.1", time.Date(123456, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))}, } // Helper function for the two tests below diff --git a/Godeps/_workspace/src/github.com/lib/pq/hstore/hstore_test.go b/Godeps/_workspace/src/github.com/lib/pq/hstore/hstore_test.go index 73a988f..8e61e69 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/hstore/hstore_test.go +++ b/Godeps/_workspace/src/github.com/lib/pq/hstore/hstore_test.go @@ -38,8 +38,7 @@ func TestHstore(t *testing.T) { // quitely create hstore if it doesn't exist _, err := db.Exec("CREATE EXTENSION IF NOT EXISTS hstore") if err != nil { - t.Log("Skipping hstore tests - hstore extension create failed. " + err.Error()) - return + t.Skipf("Skipping hstore tests - hstore extension create failed: %s", err.Error()) } hs := Hstore{} diff --git a/Godeps/_workspace/src/github.com/lib/pq/notify.go b/Godeps/_workspace/src/github.com/lib/pq/notify.go index 21aafbc..e3b08d5 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/notify.go +++ b/Godeps/_workspace/src/github.com/lib/pq/notify.go @@ -170,8 +170,6 @@ func (l *ListenerConn) listenerConnLoop() (err error) { return fmt.Errorf("unexpected message %q from server in listenerConnLoop", t) } } - - panic("not reached") } // This is the main routine for the goroutine receiving on the database @@ -318,8 +316,6 @@ func (l *ListenerConn) ExecSimpleQuery(q string) (executed bool, err error) { return false, fmt.Errorf("unknown response for simple query: %q", m.typ) } } - - panic("not reached") } func (l *ListenerConn) Close() error { @@ -428,6 +424,13 @@ func NewListener(name string, return l } +// Returns the notification channel for this listener. This is the same +// channel as Notify, and will not be recreated during the life time of the +// Listener. +func (l *Listener) NotificationChannel() <-chan *Notification { + return l.Notify +} + // Listen starts listening for notifications on a channel. Calls to this // function will block until an acknowledgement has been received from the // server. Note that Listener automatically re-establishes the connection @@ -631,8 +634,6 @@ func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notificatio return err } } - - panic("not reached") } // caller should NOT be holding l.lock diff --git a/Godeps/_workspace/src/github.com/lib/pq/notify_test.go b/Godeps/_workspace/src/github.com/lib/pq/notify_test.go index b742218..ae1208d 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/notify_test.go +++ b/Godeps/_workspace/src/github.com/lib/pq/notify_test.go @@ -24,7 +24,6 @@ func expectNotification(t *testing.T, ch <-chan *Notification, relname string, e case <-time.After(1500 * time.Millisecond): return fmt.Errorf("timeout") } - panic("not reached") } func expectNoNotification(t *testing.T, ch <-chan *Notification) error { @@ -34,7 +33,6 @@ func expectNoNotification(t *testing.T, ch <-chan *Notification) error { case <-time.After(100 * time.Millisecond): return nil } - panic("not reached") } func expectEvent(t *testing.T, eventch <-chan ListenerEventType, et ListenerEventType) error { @@ -47,7 +45,6 @@ func expectEvent(t *testing.T, eventch <-chan ListenerEventType, et ListenerEven case <-time.After(1500 * time.Millisecond): return fmt.Errorf("timeout") } - panic("not reached") } func expectNoEvent(t *testing.T, eventch <-chan ListenerEventType) error { @@ -57,7 +54,6 @@ func expectNoEvent(t *testing.T, eventch <-chan ListenerEventType) error { case <-time.After(100 * time.Millisecond): return nil } - panic("not reached") } func newTestListenerConn(t *testing.T) (*ListenerConn, <-chan *Notification) { @@ -219,8 +215,7 @@ func TestNotifyExtra(t *testing.T) { defer db.Close() if getServerVersion(t, db) < 90000 { - t.Log("skipping test due to old PG version") - return + t.Skip("skipping NOTIFY payload test since the server does not appear to support it") } l, channel := newTestListenerConn(t) diff --git a/Godeps/_workspace/src/github.com/lib/pq/ssl_test.go b/Godeps/_workspace/src/github.com/lib/pq/ssl_test.go index 36fa8b3..932b336 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/ssl_test.go +++ b/Godeps/_workspace/src/github.com/lib/pq/ssl_test.go @@ -12,21 +12,18 @@ import ( "testing" ) -func shouldSkipSSLTests(t *testing.T) bool { +func maybeSkipSSLTests(t *testing.T) { // Require some special variables for testing certificates if os.Getenv("PQSSLCERTTEST_PATH") == "" { - return true + t.Skip("PQSSLCERTTEST_PATH not set, skipping SSL tests") } value := os.Getenv("PQGOSSLTESTS") if value == "" || value == "0" { - return true - } else if value == "1" { - return false - } else { + t.Skip("PQGOSSLTESTS not enabled, skipping SSL tests") + } else if value != "1" { t.Fatalf("unexpected value %q for PQGOSSLTESTS", value) } - panic("not reached") } func openSSLConn(t *testing.T, conninfo string) (*sql.DB, error) { @@ -48,16 +45,13 @@ func checkSSLSetup(t *testing.T, conninfo string) { db, err := openSSLConn(t, conninfo) if err == nil { db.Close() - t.Fatal("expected error with conninfo=%q", conninfo) + t.Fatalf("expected error with conninfo=%q", conninfo) } } // Connect over SSL and run a simple query to test the basics func TestSSLConnection(t *testing.T) { - if shouldSkipSSLTests(t) { - t.Log("skipping SSL test") - return - } + maybeSkipSSLTests(t) // Environment sanity check: should fail without SSL checkSSLSetup(t, "sslmode=disable user=pqgossltest") @@ -74,10 +68,7 @@ func TestSSLConnection(t *testing.T) { // Test sslmode=verify-full func TestSSLVerifyFull(t *testing.T) { - if shouldSkipSSLTests(t) { - t.Log("skipping SSL test") - return - } + maybeSkipSSLTests(t) // Environment sanity check: should fail without SSL checkSSLSetup(t, "sslmode=disable user=pqgossltest") @@ -94,7 +85,7 @@ func TestSSLVerifyFull(t *testing.T) { rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt") rootCert := "sslrootcert=" + rootCertPath + " " // No match on Common Name - _, err = openSSLConn(t, rootCert + "host=127.0.0.1 sslmode=verify-full user=pqgossltest") + _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-full user=pqgossltest") if err == nil { t.Fatal("expected error") } @@ -103,7 +94,7 @@ func TestSSLVerifyFull(t *testing.T) { t.Fatalf("expected x509.HostnameError, got %#+v", err) } // OK - _, err = openSSLConn(t, rootCert + "host=postgres sslmode=verify-full user=pqgossltest") + _, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-full user=pqgossltest") if err != nil { t.Fatal(err) } @@ -111,10 +102,7 @@ func TestSSLVerifyFull(t *testing.T) { // Test sslmode=verify-ca func TestSSLVerifyCA(t *testing.T) { - if shouldSkipSSLTests(t) { - t.Log("skipping SSL test") - return - } + maybeSkipSSLTests(t) // Environment sanity check: should fail without SSL checkSSLSetup(t, "sslmode=disable user=pqgossltest") @@ -131,18 +119,17 @@ func TestSSLVerifyCA(t *testing.T) { rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt") rootCert := "sslrootcert=" + rootCertPath + " " // No match on Common Name, but that's OK - _, err = openSSLConn(t, rootCert + "host=127.0.0.1 sslmode=verify-ca user=pqgossltest") + _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-ca user=pqgossltest") if err != nil { t.Fatal(err) } // Everything OK - _, err = openSSLConn(t, rootCert + "host=postgres sslmode=verify-ca user=pqgossltest") + _, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-ca user=pqgossltest") if err != nil { t.Fatal(err) } } - func getCertConninfo(t *testing.T, source string) string { var sslkey string var sslcert string @@ -170,10 +157,7 @@ func getCertConninfo(t *testing.T, source string) string { // Authenticate over SSL using client certificates func TestSSLClientCertificates(t *testing.T) { - if shouldSkipSSLTests(t) { - t.Log("skipping SSL test") - return - } + maybeSkipSSLTests(t) // Environment sanity check: should fail without SSL checkSSLSetup(t, "sslmode=disable user=pqgossltest") @@ -205,10 +189,7 @@ func TestSSLClientCertificates(t *testing.T) { // Test errors with ssl certificates func TestSSLClientCertificatesMissingFiles(t *testing.T) { - if shouldSkipSSLTests(t) { - t.Log("skipping SSL test") - return - } + maybeSkipSSLTests(t) // Environment sanity check: should fail without SSL checkSSLSetup(t, "sslmode=disable user=pqgossltest") diff --git a/Godeps/_workspace/src/github.com/lib/pq/user_posix.go b/Godeps/_workspace/src/github.com/lib/pq/user_posix.go index aa5a3da..e937d7d 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/user_posix.go +++ b/Godeps/_workspace/src/github.com/lib/pq/user_posix.go @@ -1,15 +1,24 @@ // Package pq is a pure Go Postgres driver for the database/sql package. -// +build darwin freebsd linux nacl netbsd openbsd solaris +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris package pq -import "os/user" +import ( + "os" + "os/user" +) func userCurrent() (string, error) { u, err := user.Current() - if err != nil { - return "", err + if err == nil { + return u.Username, nil } - return u.Username, nil + + name := os.Getenv("USER") + if name != "" { + return name, nil + } + + return "", ErrCouldNotDetectUsername } diff --git a/Godeps/_workspace/src/github.com/lib/pq/user_windows.go b/Godeps/_workspace/src/github.com/lib/pq/user_windows.go index a7593ff..2b69126 100644 --- a/Godeps/_workspace/src/github.com/lib/pq/user_windows.go +++ b/Godeps/_workspace/src/github.com/lib/pq/user_windows.go @@ -19,7 +19,7 @@ func userCurrent() (string, error) { pwname_size := uint32(len(pw_name)) - 1 err := syscall.GetUserNameEx(syscall.NameSamCompatible, &pw_name[0], &pwname_size) if err != nil { - return "", err + return "", ErrCouldNotDetectUsername } s := syscall.UTF16ToString(pw_name) u := filepath.Base(s) diff --git a/Godeps/_workspace/src/github.com/stretchr/testify/assert/assertions.go b/Godeps/_workspace/src/github.com/stretchr/testify/assert/assertions.go index a7a24e7..780b172 100644 --- a/Godeps/_workspace/src/github.com/stretchr/testify/assert/assertions.go +++ b/Godeps/_workspace/src/github.com/stretchr/testify/assert/assertions.go @@ -37,10 +37,10 @@ func ObjectsAreEqual(expected, actual interface{}) bool { } actualType := reflect.TypeOf(actual) - if reflect.TypeOf(actual).ConvertibleTo(reflect.TypeOf(expected)) { + if actualType.ConvertibleTo(reflect.TypeOf(expected)) { expectedValue := reflect.ValueOf(expected) // Attempt comparison after type conversion - if actual == expectedValue.Convert(actualType).Interface() { + if reflect.DeepEqual(actual, expectedValue.Convert(actualType).Interface()) { return true } } diff --git a/Godeps/_workspace/src/github.com/stretchr/testify/assert/assertions_test.go b/Godeps/_workspace/src/github.com/stretchr/testify/assert/assertions_test.go index bb63be2..db447d3 100644 --- a/Godeps/_workspace/src/github.com/stretchr/testify/assert/assertions_test.go +++ b/Godeps/_workspace/src/github.com/stretchr/testify/assert/assertions_test.go @@ -40,6 +40,9 @@ func TestObjectsAreEqual(t *testing.T) { if !ObjectsAreEqual(nil, nil) { t.Error("objectsAreEqual should return true") } + if ObjectsAreEqual(map[int]int{5: 10}, map[int]int{10: 20}) { + t.Error("objectsAreEqual should return false") + } } @@ -94,6 +97,10 @@ func TestEqual(t *testing.T) { if !Equal(mockT, int64(123), uint64(123)) { t.Error("Equal should return true") } + funcA := func() int { return 42 } + if !Equal(mockT, funcA, funcA) { + t.Error("Equal should return true") + } } @@ -196,6 +203,11 @@ func TestNotEqual(t *testing.T) { if !NotEqual(mockT, nil, new(AssertionTesterConformingObject)) { t.Error("NotEqual should return true") } + funcA := func() int { return 23 } + funcB := func() int { return 42 } + if !NotEqual(mockT, funcA, funcB) { + t.Error("NotEqual should return true") + } if NotEqual(mockT, "Hello World", "Hello World") { t.Error("NotEqual should return false") @@ -223,10 +235,10 @@ func TestContains(t *testing.T) { mockT := new(testing.T) list := []string{"Foo", "Bar"} complexList := []*A{ - &A{"b", "c"}, - &A{"d", "e"}, - &A{"g", "h"}, - &A{"j", "k"}, + {"b", "c"}, + {"d", "e"}, + {"g", "h"}, + {"j", "k"}, } if !Contains(mockT, "Hello World", "Hello") {