Explorar o código

better TIME / DATE / DATETIME fields handling

* The output format of TIME fields changed from "HH:MM:SS" to
"[-][H]HH:MM:SS[.fractal]". See
http://dev.mysql.com/doc/refman/5.6/en/time.html for details
* NULL values in DATE, NEWDATE, TIME, TIMESTAMP and DATETIME fields are
now handeled correctly
* DATETIME and TIMESTAMP flieds now may contain a trailing fractional
seconds part in up to microseconds (6 digits). See
http://dev.mysql.com/doc/refman/5.6/en/datetime.html for details
Julien Schmidt %!s(int64=13) %!d(string=hai) anos
pai
achega
ece91a93ce
Modificáronse 1 ficheiros con 95 adicións e 31 borrados
  1. 95 31
      packets.go

+ 95 - 31
packets.go

@@ -828,16 +828,22 @@ func (rc *mysqlRows) readBinaryRow(dest []driver.Value) (err error) {
 		// Date YYYY-MM-DD
 		case FIELD_TYPE_DATE, FIELD_TYPE_NEWDATE:
 			var num uint64
-			// TODO(js): allow nil values
-			num, _, n, err = readLengthEncodedInteger(data[pos:])
+			var isNull bool
+			num, isNull, n, err = readLengthEncodedInteger(data[pos:])
 			if err != nil {
 				return
 			}
 
 			if num == 0 {
-				dest[i] = []byte("0000-00-00")
-				pos += n
-				continue
+				if isNull {
+					dest[i] = nil
+					pos++ // n = 1
+					continue
+				} else {
+					dest[i] = []byte("0000-00-00")
+					pos++ // n = 1
+					continue
+				}
 			} else {
 				dest[i] = []byte(fmt.Sprintf("%04d-%02d-%02d",
 					binary.LittleEndian.Uint16(data[pos:pos+2]),
@@ -847,62 +853,120 @@ func (rc *mysqlRows) readBinaryRow(dest []driver.Value) (err error) {
 				continue
 			}
 
-		// Time HH:MM:SS
+		// Time [-][H]HH:MM:SS[.fractal]
 		case FIELD_TYPE_TIME:
 			var num uint64
-			// TODO(js): allow nil values
-			num, _, n, err = readLengthEncodedInteger(data[pos:])
+			var isNull bool
+			num, isNull, n, err = readLengthEncodedInteger(data[pos:])
 			if err != nil {
 				return
 			}
 
 			if num == 0 {
-				dest[i] = []byte("00:00:00")
-				pos += n
+				if isNull {
+					dest[i] = nil
+					pos++ // n = 1
+					continue
+				} else {
+					dest[i] = []byte("00:00:00")
+					pos++ // n = 1
+					continue
+				}
+			}
+
+			pos += n
+
+			var sign byte
+			if data[pos] == 1 {
+				sign = byte('-')
+			}
+
+			switch num {
+			case 8:
+				dest[i] = []byte(fmt.Sprintf(
+					"%c%02d:%02d:%02d",
+					sign,
+					uint16(data[pos+1])*24+uint16(data[pos+5]),
+					data[pos+6],
+					data[pos+7],
+				))
+				pos += 8
 				continue
-			} else {
-				dest[i] = []byte(fmt.Sprintf("%02d:%02d:%02d",
+			case 12:
+				dest[i] = []byte(fmt.Sprintf(
+					"%c%02d:%02d:%02d.%06d",
+					sign,
+					uint16(data[pos+1])*24+uint16(data[pos+5]),
 					data[pos+6],
 					data[pos+7],
-					data[pos+8]))
-				pos += n + int(num)
+					binary.LittleEndian.Uint32(data[pos+8:pos+12]),
+				))
+				pos += 12
 				continue
+			default:
+				return fmt.Errorf("Invalid TIME-packet length %d", num)
 			}
 
-		// Timestamp YYYY-MM-DD HH:MM:SS
+		// Timestamp YYYY-MM-DD HH:MM:SS[.fractal]
 		case FIELD_TYPE_TIMESTAMP, FIELD_TYPE_DATETIME:
 			var num uint64
-			// TODO(js): allow nil values
-			num, _, n, err = readLengthEncodedInteger(data[pos:])
+			var isNull bool
+			num, isNull, n, err = readLengthEncodedInteger(data[pos:])
 			if err != nil {
 				return
 			}
 
+			if num == 0 {
+				if isNull {
+					dest[i] = nil
+					pos++ // n = 1
+					continue
+				} else {
+					dest[i] = []byte("0000-00-00 00:00:00")
+					pos++ // n = 1
+					continue
+				}
+			}
+
+			pos += n
+
 			switch num {
-			case 0:
-				dest[i] = []byte("0000-00-00 00:00:00")
-				pos += n
-				continue
 			case 4:
-				dest[i] = []byte(fmt.Sprintf("%04d-%02d-%02d 00:00:00",
+				dest[i] = []byte(fmt.Sprintf(
+					"%04d-%02d-%02d 00:00:00",
 					binary.LittleEndian.Uint16(data[pos:pos+2]),
 					data[pos+2],
-					data[pos+3]))
-				pos += n + int(num)
+					data[pos+3],
+				))
+				pos += 5
 				continue
-			default:
-				if num < 7 {
-					return fmt.Errorf("Invalid datetime-packet length %d", num)
-				}
-				dest[i] = []byte(fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d",
+			case 7:
+				dest[i] = []byte(fmt.Sprintf(
+					"%04d-%02d-%02d %02d:%02d:%02d",
 					binary.LittleEndian.Uint16(data[pos:pos+2]),
 					data[pos+2],
 					data[pos+3],
 					data[pos+4],
 					data[pos+5],
-					data[pos+6]))
-				pos += n + int(num)
+					data[pos+6],
+				))
+				pos += 7
 				continue
+			case 11:
+				dest[i] = []byte(fmt.Sprintf(
+					"%04d-%02d-%02d %02d:%02d:%02d.%06d",
+					binary.LittleEndian.Uint16(data[pos:pos+2]),
+					data[pos+2],
+					data[pos+3],
+					data[pos+4],
+					data[pos+5],
+					data[pos+6],
+					binary.LittleEndian.Uint32(data[pos+7:pos+11]),
+				))
+				pos += 11
+				continue
+			default:
+				return fmt.Errorf("Invalid DATETIME-packet length %d", num)
 			}
 
 		// Please report if this happens!