summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/lib/pq/encode.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/lib/pq/encode.go')
-rw-r--r--vendor/github.com/lib/pq/encode.go117
1 files changed, 68 insertions, 49 deletions
diff --git a/vendor/github.com/lib/pq/encode.go b/vendor/github.com/lib/pq/encode.go
index 6681bd3e7..29e8f6ff7 100644
--- a/vendor/github.com/lib/pq/encode.go
+++ b/vendor/github.com/lib/pq/encode.go
@@ -56,10 +56,13 @@ func encode(parameterStatus *parameterStatus, x interface{}, pgtypOid oid.Oid) [
}
func decode(parameterStatus *parameterStatus, s []byte, typ oid.Oid, f format) interface{} {
- if f == formatBinary {
+ switch f {
+ case formatBinary:
return binaryDecode(parameterStatus, s, typ)
- } else {
+ case formatText:
return textDecode(parameterStatus, s, typ)
+ default:
+ panic("not reached")
}
}
@@ -83,8 +86,14 @@ func binaryDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) inter
func textDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{} {
switch typ {
+ case oid.T_char, oid.T_varchar, oid.T_text:
+ return string(s)
case oid.T_bytea:
- return parseBytea(s)
+ b, err := parseBytea(s)
+ if err != nil {
+ errorf("%s", err)
+ }
+ return b
case oid.T_timestamptz:
return parseTs(parameterStatus.currentLocation, string(s))
case oid.T_timestamp, oid.T_date:
@@ -195,21 +204,21 @@ func mustParse(f string, typ oid.Oid, s []byte) time.Time {
return t
}
-var invalidTimestampErr = errors.New("invalid timestamp")
+var errInvalidTimestamp = errors.New("invalid timestamp")
type timestampParser struct {
err error
}
-func (p *timestampParser) expect(str, char string, pos int) {
+func (p *timestampParser) expect(str string, char byte, pos int) {
if p.err != nil {
return
}
if pos+1 > len(str) {
- p.err = invalidTimestampErr
+ p.err = errInvalidTimestamp
return
}
- if c := str[pos : pos+1]; c != char && p.err == nil {
+ if c := str[pos]; c != char && p.err == nil {
p.err = fmt.Errorf("expected '%v' at position %v; got '%v'", char, pos, c)
}
}
@@ -219,7 +228,7 @@ func (p *timestampParser) mustAtoi(str string, begin int, end int) int {
return 0
}
if begin < 0 || end < 0 || begin > end || end > len(str) {
- p.err = invalidTimestampErr
+ p.err = errInvalidTimestamp
return 0
}
result, err := strconv.Atoi(str[begin:end])
@@ -242,7 +251,7 @@ type locationCache struct {
// about 5% speed could be gained by putting the cache in the connection and
// losing the mutex, at the cost of a small amount of memory and a somewhat
// significant increase in code complexity.
-var globalLocationCache *locationCache = newLocationCache()
+var globalLocationCache = newLocationCache()
func newLocationCache() *locationCache {
return &locationCache{cache: make(map[int]*time.Location)}
@@ -272,26 +281,26 @@ const (
infinityTsNegativeMustBeSmaller = "pq: infinity timestamp: negative value must be smaller (before) than positive"
)
-/*
- * If EnableInfinityTs is not called, "-infinity" and "infinity" will return
- * []byte("-infinity") and []byte("infinity") respectively, and potentially
- * cause error "sql: Scan error on column index 0: unsupported driver -> Scan pair: []uint8 -> *time.Time",
- * when scanning into a time.Time value.
- *
- * Once EnableInfinityTs has been called, all connections created using this
- * driver will decode Postgres' "-infinity" and "infinity" for "timestamp",
- * "timestamp with time zone" and "date" types to the predefined minimum and
- * maximum times, respectively. When encoding time.Time values, any time which
- * equals or precedes the predefined minimum time will be encoded to
- * "-infinity". Any values at or past the maximum time will similarly be
- * encoded to "infinity".
- *
- *
- * If EnableInfinityTs is called with negative >= positive, it will panic.
- * Calling EnableInfinityTs after a connection has been established results in
- * undefined behavior. If EnableInfinityTs is called more than once, it will
- * panic.
- */
+// EnableInfinityTs controls the handling of Postgres' "-infinity" and
+// "infinity" "timestamp"s.
+//
+// If EnableInfinityTs is not called, "-infinity" and "infinity" will return
+// []byte("-infinity") and []byte("infinity") respectively, and potentially
+// cause error "sql: Scan error on column index 0: unsupported driver -> Scan
+// pair: []uint8 -> *time.Time", when scanning into a time.Time value.
+//
+// Once EnableInfinityTs has been called, all connections created using this
+// driver will decode Postgres' "-infinity" and "infinity" for "timestamp",
+// "timestamp with time zone" and "date" types to the predefined minimum and
+// maximum times, respectively. When encoding time.Time values, any time which
+// equals or precedes the predefined minimum time will be encoded to
+// "-infinity". Any values at or past the maximum time will similarly be
+// encoded to "infinity".
+//
+// If EnableInfinityTs is called with negative >= positive, it will panic.
+// Calling EnableInfinityTs after a connection has been established results in
+// undefined behavior. If EnableInfinityTs is called more than once, it will
+// panic.
func EnableInfinityTs(negative time.Time, positive time.Time) {
if infinityTsEnabled {
panic(infinityTsEnabledAlready)
@@ -335,6 +344,10 @@ func parseTs(currentLocation *time.Location, str string) interface{} {
return t
}
+// ParseTimestamp parses Postgres' text format. It returns a time.Time in
+// currentLocation iff that time's offset agrees with the offset sent from the
+// Postgres server. Otherwise, ParseTimestamp returns a time.Time with the
+// fixed offset offset provided by the Postgres server.
func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, error) {
p := timestampParser{}
@@ -344,18 +357,18 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro
year := p.mustAtoi(str, 0, monSep)
daySep := monSep + 3
month := p.mustAtoi(str, monSep+1, daySep)
- p.expect(str, "-", daySep)
+ p.expect(str, '-', daySep)
timeSep := daySep + 3
day := p.mustAtoi(str, daySep+1, timeSep)
var hour, minute, second int
if len(str) > monSep+len("01-01")+1 {
- p.expect(str, " ", timeSep)
+ p.expect(str, ' ', timeSep)
minSep := timeSep + 3
- p.expect(str, ":", minSep)
+ p.expect(str, ':', minSep)
hour = p.mustAtoi(str, timeSep+1, minSep)
secSep := minSep + 3
- p.expect(str, ":", secSep)
+ p.expect(str, ':', secSep)
minute = p.mustAtoi(str, minSep+1, secSep)
secEnd := secSep + 3
second = p.mustAtoi(str, secSep+1, secEnd)
@@ -369,7 +382,7 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro
nanoSec := 0
tzOff := 0
- if remainderIdx+1 <= len(str) && str[remainderIdx:remainderIdx+1] == "." {
+ if remainderIdx < len(str) && str[remainderIdx] == '.' {
fracStart := remainderIdx + 1
fracOff := strings.IndexAny(str[fracStart:], "-+ ")
if fracOff < 0 {
@@ -380,25 +393,26 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro
remainderIdx += fracOff + 1
}
- if tzStart := remainderIdx; tzStart+1 <= len(str) && (str[tzStart:tzStart+1] == "-" || str[tzStart:tzStart+1] == "+") {
+ if tzStart := remainderIdx; tzStart < len(str) && (str[tzStart] == '-' || str[tzStart] == '+') {
// time zone separator is always '-' or '+' (UTC is +00)
var tzSign int
- if c := str[tzStart : tzStart+1]; c == "-" {
+ switch c := str[tzStart]; c {
+ case '-':
tzSign = -1
- } else if c == "+" {
+ case '+':
tzSign = +1
- } else {
+ default:
return time.Time{}, fmt.Errorf("expected '-' or '+' at position %v; got %v", tzStart, c)
}
tzHours := p.mustAtoi(str, tzStart+1, tzStart+3)
remainderIdx += 3
var tzMin, tzSec int
- if tzStart+4 <= len(str) && str[tzStart+3:tzStart+4] == ":" {
- tzMin = p.mustAtoi(str, tzStart+4, tzStart+6)
+ if remainderIdx < len(str) && str[remainderIdx] == ':' {
+ tzMin = p.mustAtoi(str, remainderIdx+1, remainderIdx+3)
remainderIdx += 3
}
- if tzStart+7 <= len(str) && str[tzStart+6:tzStart+7] == ":" {
- tzSec = p.mustAtoi(str, tzStart+7, tzStart+9)
+ if remainderIdx < len(str) && str[remainderIdx] == ':' {
+ tzSec = p.mustAtoi(str, remainderIdx+1, remainderIdx+3)
remainderIdx += 3
}
tzOff = tzSign * ((tzHours * 60 * 60) + (tzMin * 60) + tzSec)
@@ -432,7 +446,7 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro
}
// formatTs formats t into a format postgres understands.
-func formatTs(t time.Time) (b []byte) {
+func formatTs(t time.Time) []byte {
if infinityTsEnabled {
// t <= -infinity : ! (t > -infinity)
if !t.After(infinityTsNegative) {
@@ -443,6 +457,11 @@ func formatTs(t time.Time) (b []byte) {
return []byte("infinity")
}
}
+ return FormatTimestamp(t)
+}
+
+// FormatTimestamp formats t into Postgres' text format for timestamps.
+func FormatTimestamp(t time.Time) []byte {
// Need to send dates before 0001 A.D. with " BC" suffix, instead of the
// minus sign preferred by Go.
// Beware, "0000" in ISO is "1 BC", "-0001" is "2 BC" and so on
@@ -452,7 +471,7 @@ func formatTs(t time.Time) (b []byte) {
t = t.AddDate((-t.Year())*2+1, 0, 0)
bc = true
}
- b = []byte(t.Format(time.RFC3339Nano))
+ b := []byte(t.Format(time.RFC3339Nano))
_, offset := t.Zone()
offset = offset % 60
@@ -477,14 +496,14 @@ func formatTs(t time.Time) (b []byte) {
// Parse a bytea value received from the server. Both "hex" and the legacy
// "escape" format are supported.
-func parseBytea(s []byte) (result []byte) {
+func parseBytea(s []byte) (result []byte, err error) {
if len(s) >= 2 && bytes.Equal(s[:2], []byte("\\x")) {
// bytea_output = hex
s = s[2:] // trim off leading "\\x"
result = make([]byte, hex.DecodedLen(len(s)))
_, err := hex.Decode(result, s)
if err != nil {
- errorf("%s", err)
+ return nil, err
}
} else {
// bytea_output = escape
@@ -499,11 +518,11 @@ func parseBytea(s []byte) (result []byte) {
// '\\' followed by an octal number
if len(s) < 4 {
- errorf("invalid bytea sequence %v", s)
+ return nil, fmt.Errorf("invalid bytea sequence %v", s)
}
r, err := strconv.ParseInt(string(s[1:4]), 8, 9)
if err != nil {
- errorf("could not parse bytea value: %s", err.Error())
+ return nil, fmt.Errorf("could not parse bytea value: %s", err.Error())
}
result = append(result, byte(r))
s = s[4:]
@@ -521,7 +540,7 @@ func parseBytea(s []byte) (result []byte) {
}
}
- return result
+ return result, nil
}
func encodeBytea(serverVersion int, v []byte) (result []byte) {