github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/pgwire/types.go (about) 1 // Copyright 2015 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package pgwire 12 13 import ( 14 "context" 15 "encoding/binary" 16 "math" 17 "math/big" 18 "net" 19 "strconv" 20 "strings" 21 "time" 22 23 "github.com/cockroachdb/apd" 24 "github.com/cockroachdb/cockroach/pkg/server/telemetry" 25 "github.com/cockroachdb/cockroach/pkg/sql/lex" 26 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgwirebase" 27 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 28 "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" 29 "github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry" 30 "github.com/cockroachdb/cockroach/pkg/sql/types" 31 "github.com/cockroachdb/cockroach/pkg/util/duration" 32 "github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented" 33 "github.com/cockroachdb/cockroach/pkg/util/ipaddr" 34 "github.com/cockroachdb/cockroach/pkg/util/log" 35 "github.com/cockroachdb/cockroach/pkg/util/timeofday" 36 "github.com/cockroachdb/cockroach/pkg/util/timetz" 37 "github.com/cockroachdb/errors" 38 "github.com/lib/pq/oid" 39 ) 40 41 // pgType contains type metadata used in RowDescription messages. 42 type pgType struct { 43 oid oid.Oid 44 45 // Variable-size types have size=-1. 46 // Note that the protocol has both int16 and int32 size fields, 47 // so this attribute is an unsized int and should be cast 48 // as needed. 49 // This field does *not* correspond to the encoded length of a 50 // data type, so it's unclear what, if anything, it is used for. 51 // To get the right value, "SELECT oid, typlen FROM pg_type" 52 // on a postgres server. 53 size int 54 } 55 56 func pgTypeForParserType(t *types.T) pgType { 57 size := -1 58 if s, variable := tree.DatumTypeSize(t); !variable { 59 size = int(s) 60 } 61 return pgType{ 62 oid: t.Oid(), 63 size: size, 64 } 65 } 66 67 func (b *writeBuffer) writeTextDatum( 68 ctx context.Context, d tree.Datum, conv sessiondata.DataConversionConfig, 69 ) { 70 if log.V(2) { 71 log.Infof(ctx, "pgwire writing TEXT datum of type: %T, %#v", d, d) 72 } 73 if d == tree.DNull { 74 // NULL is encoded as -1; all other values have a length prefix. 75 b.putInt32(-1) 76 return 77 } 78 switch v := tree.UnwrapDatum(nil, d).(type) { 79 case *tree.DBitArray: 80 b.textFormatter.FormatNode(v) 81 b.writeFromFmtCtx(b.textFormatter) 82 83 case *tree.DBool: 84 b.textFormatter.FormatNode(v) 85 b.writeFromFmtCtx(b.textFormatter) 86 87 case *tree.DInt: 88 // Start at offset 4 because `putInt32` clobbers the first 4 bytes. 89 s := strconv.AppendInt(b.putbuf[4:4], int64(*v), 10) 90 b.putInt32(int32(len(s))) 91 b.write(s) 92 93 case *tree.DFloat: 94 // Start at offset 4 because `putInt32` clobbers the first 4 bytes. 95 s := strconv.AppendFloat(b.putbuf[4:4], float64(*v), 'g', conv.GetFloatPrec(), 64) 96 b.putInt32(int32(len(s))) 97 b.write(s) 98 99 case *tree.DDecimal: 100 b.writeLengthPrefixedDatum(v) 101 102 case *tree.DBytes: 103 result := lex.EncodeByteArrayToRawBytes( 104 string(*v), conv.BytesEncodeFormat, false /* skipHexPrefix */) 105 b.putInt32(int32(len(result))) 106 b.write([]byte(result)) 107 108 case *tree.DUuid: 109 // Start at offset 4 because `putInt32` clobbers the first 4 bytes. 110 s := b.putbuf[4 : 4+36] 111 v.UUID.StringBytes(s) 112 b.putInt32(int32(len(s))) 113 b.write(s) 114 115 case *tree.DIPAddr: 116 b.writeLengthPrefixedString(v.IPAddr.String()) 117 118 case *tree.DString: 119 b.writeLengthPrefixedString(string(*v)) 120 121 case *tree.DCollatedString: 122 b.writeLengthPrefixedString(v.Contents) 123 124 case *tree.DDate: 125 b.textFormatter.FormatNode(v) 126 b.writeFromFmtCtx(b.textFormatter) 127 128 case *tree.DTime: 129 // Start at offset 4 because `putInt32` clobbers the first 4 bytes. 130 s := formatTime(timeofday.TimeOfDay(*v), b.putbuf[4:4]) 131 b.putInt32(int32(len(s))) 132 b.write(s) 133 134 case *tree.DTimeTZ: 135 // Start at offset 4 because `putInt32` clobbers the first 4 bytes. 136 s := formatTimeTZ(v.TimeTZ, b.putbuf[4:4]) 137 b.putInt32(int32(len(s))) 138 b.write(s) 139 140 case *tree.DGeography: 141 s := v.Geography.EWKBHex() 142 b.putInt32(int32(len(s))) 143 b.write([]byte(s)) 144 145 case *tree.DGeometry: 146 s := v.Geometry.EWKBHex() 147 b.putInt32(int32(len(s))) 148 b.write([]byte(s)) 149 150 case *tree.DTimestamp: 151 // Start at offset 4 because `putInt32` clobbers the first 4 bytes. 152 s := formatTs(v.Time, nil, b.putbuf[4:4]) 153 b.putInt32(int32(len(s))) 154 b.write(s) 155 156 case *tree.DTimestampTZ: 157 // Start at offset 4 because `putInt32` clobbers the first 4 bytes. 158 s := formatTs(v.Time, conv.Location, b.putbuf[4:4]) 159 b.putInt32(int32(len(s))) 160 b.write(s) 161 162 case *tree.DInterval: 163 b.textFormatter.FormatNode(v) 164 b.writeFromFmtCtx(b.textFormatter) 165 166 case *tree.DJSON: 167 b.writeLengthPrefixedString(v.JSON.String()) 168 169 case *tree.DTuple: 170 b.textFormatter.FormatNode(v) 171 b.writeFromFmtCtx(b.textFormatter) 172 173 case *tree.DArray: 174 // Arrays have custom formatting depending on their OID. 175 b.textFormatter.FormatNode(d) 176 b.writeFromFmtCtx(b.textFormatter) 177 178 case *tree.DOid: 179 b.writeLengthPrefixedDatum(v) 180 181 case *tree.DEnum: 182 // Enums are serialized with their logical representation. 183 b.writeLengthPrefixedString(v.LogicalRep) 184 185 default: 186 b.setError(errors.Errorf("unsupported type %T", d)) 187 } 188 } 189 190 // writeBinaryDatum writes d to the buffer. Oid must be specified for types 191 // that have various width encodings. It is ignored (and can be 0) for types 192 // with a 1:1 datum:oid mapping. 193 func (b *writeBuffer) writeBinaryDatum( 194 ctx context.Context, d tree.Datum, sessionLoc *time.Location, Oid oid.Oid, 195 ) { 196 if log.V(2) { 197 log.Infof(ctx, "pgwire writing BINARY datum of type: %T, %#v", d, d) 198 } 199 if d == tree.DNull { 200 // NULL is encoded as -1; all other values have a length prefix. 201 b.putInt32(-1) 202 return 203 } 204 switch v := tree.UnwrapDatum(nil, d).(type) { 205 case *tree.DBitArray: 206 words, lastBitsUsed := v.EncodingParts() 207 if len(words) == 0 { 208 b.putInt32(4) 209 } else { 210 // Encode the length of the output bytes. It is computed here so we don't 211 // have to keep a buffer. 212 // 4: the int32 of the bitLen. 213 // 8*(len(words)-1): number of 8-byte words except the last one since it's 214 // partial. 215 // (lastBitsUsed+7)/8: number of bytes that will be written in the last 216 // partial word. The /8 rounds down, such that the +7 will cause 1-or-more 217 // bits to use a byte, but 0 will not. 218 b.putInt32(4 + int32(8*(len(words)-1)) + int32((lastBitsUsed+7)/8)) 219 } 220 bitLen := v.BitLen() 221 b.putInt32(int32(bitLen)) 222 var byteBuf [8]byte 223 for i := 0; i < len(words)-1; i++ { 224 w := words[i] 225 binary.BigEndian.PutUint64(byteBuf[:], w) 226 b.write(byteBuf[:]) 227 } 228 if len(words) > 0 { 229 w := words[len(words)-1] 230 for i := uint(0); i < uint(lastBitsUsed); i += 8 { 231 c := byte(w >> (56 - i)) 232 b.writeByte(c) 233 } 234 } 235 236 case *tree.DBool: 237 b.putInt32(1) 238 if *v { 239 b.writeByte(1) 240 } else { 241 b.writeByte(0) 242 } 243 244 case *tree.DInt: 245 switch Oid { 246 case oid.T_int2: 247 b.putInt32(2) 248 b.putInt16(int16(*v)) 249 case oid.T_int4: 250 b.putInt32(4) 251 b.putInt32(int32(*v)) 252 case oid.T_int8: 253 b.putInt32(8) 254 b.putInt64(int64(*v)) 255 default: 256 b.setError(errors.Errorf("unsupported int oid: %v", Oid)) 257 } 258 259 case *tree.DFloat: 260 switch Oid { 261 case oid.T_float4: 262 b.putInt32(4) 263 b.putInt32(int32(math.Float32bits(float32(*v)))) 264 case oid.T_float8: 265 b.putInt32(8) 266 b.putInt64(int64(math.Float64bits(float64(*v)))) 267 default: 268 b.setError(errors.Errorf("unsupported float oid: %v", Oid)) 269 } 270 271 case *tree.DDecimal: 272 if v.Form != apd.Finite { 273 b.putInt32(8) 274 // 0 digits. 275 b.putInt32(0) 276 // https://github.com/postgres/postgres/blob/ffa4cbd623dd69f9fa99e5e92426928a5782cf1a/src/backend/utils/adt/numeric.c#L169 277 b.write([]byte{0xc0, 0, 0, 0}) 278 279 if v.Form == apd.Infinite { 280 // TODO(mjibson): #32489 281 // The above encoding is not correct for Infinity, but since that encoding 282 // doesn't exist in postgres, it's unclear what to do. For now use the NaN 283 // encoding and count it to see if anyone even needs this. 284 telemetry.Inc(sqltelemetry.BinaryDecimalInfinityCounter) 285 } 286 287 return 288 } 289 290 alloc := struct { 291 pgNum pgwirebase.PGNumeric 292 293 bigI big.Int 294 }{ 295 pgNum: pgwirebase.PGNumeric{ 296 // Since we use 2000 as the exponent limits in tree.DecimalCtx, this 297 // conversion should not overflow. 298 Dscale: int16(-v.Exponent), 299 }, 300 } 301 302 if v.Sign() >= 0 { 303 alloc.pgNum.Sign = pgwirebase.PGNumericPos 304 } else { 305 alloc.pgNum.Sign = pgwirebase.PGNumericNeg 306 } 307 308 isZero := func(r rune) bool { 309 return r == '0' 310 } 311 312 // Mostly cribbed from libpqtypes' str2num. 313 digits := strings.TrimLeftFunc(alloc.bigI.Abs(&v.Coeff).String(), isZero) 314 dweight := len(digits) - int(alloc.pgNum.Dscale) - 1 315 digits = strings.TrimRightFunc(digits, isZero) 316 317 if dweight >= 0 { 318 alloc.pgNum.Weight = int16((dweight+1+pgwirebase.PGDecDigits-1)/pgwirebase.PGDecDigits - 1) 319 } else { 320 alloc.pgNum.Weight = int16(-((-dweight-1)/pgwirebase.PGDecDigits + 1)) 321 } 322 offset := (int(alloc.pgNum.Weight)+1)*pgwirebase.PGDecDigits - (dweight + 1) 323 alloc.pgNum.Ndigits = int16((len(digits) + offset + pgwirebase.PGDecDigits - 1) / pgwirebase.PGDecDigits) 324 325 if len(digits) == 0 { 326 offset = 0 327 alloc.pgNum.Ndigits = 0 328 alloc.pgNum.Weight = 0 329 } 330 331 digitIdx := -offset 332 333 nextDigit := func() int16 { 334 var ndigit int16 335 for nextDigitIdx := digitIdx + pgwirebase.PGDecDigits; digitIdx < nextDigitIdx; digitIdx++ { 336 ndigit *= 10 337 if digitIdx >= 0 && digitIdx < len(digits) { 338 ndigit += int16(digits[digitIdx] - '0') 339 } 340 } 341 return ndigit 342 } 343 344 b.putInt32(int32(2 * (4 + alloc.pgNum.Ndigits))) 345 b.putInt16(alloc.pgNum.Ndigits) 346 b.putInt16(alloc.pgNum.Weight) 347 b.putInt16(int16(alloc.pgNum.Sign)) 348 b.putInt16(alloc.pgNum.Dscale) 349 350 for digitIdx < len(digits) { 351 b.putInt16(nextDigit()) 352 } 353 354 case *tree.DBytes: 355 b.putInt32(int32(len(*v))) 356 b.write([]byte(*v)) 357 358 case *tree.DUuid: 359 b.putInt32(16) 360 b.write(v.GetBytes()) 361 362 case *tree.DIPAddr: 363 // We calculate the Postgres binary format for an IPAddr. For the spec see, 364 // https://github.com/postgres/postgres/blob/81c5e46c490e2426db243eada186995da5bb0ba7/src/backend/utils/adt/network.c#L144 365 // The pgBinary encoding is as follows: 366 // The int32 length of the following bytes. 367 // The family byte. 368 // The mask size byte. 369 // A 0 byte for is_cidr. It's ignored on the postgres frontend. 370 // The length of our IP bytes. 371 // The IP bytes. 372 const pgIPAddrBinaryHeaderSize = 4 373 if v.Family == ipaddr.IPv4family { 374 b.putInt32(net.IPv4len + pgIPAddrBinaryHeaderSize) 375 b.writeByte(pgwirebase.PGBinaryIPv4family) 376 b.writeByte(v.Mask) 377 b.writeByte(0) 378 b.writeByte(byte(net.IPv4len)) 379 err := v.Addr.WriteIPv4Bytes(b) 380 if err != nil { 381 b.setError(err) 382 } 383 } else if v.Family == ipaddr.IPv6family { 384 b.putInt32(net.IPv6len + pgIPAddrBinaryHeaderSize) 385 b.writeByte(pgwirebase.PGBinaryIPv6family) 386 b.writeByte(v.Mask) 387 b.writeByte(0) 388 b.writeByte(byte(net.IPv6len)) 389 err := v.Addr.WriteIPv6Bytes(b) 390 if err != nil { 391 b.setError(err) 392 } 393 } else { 394 b.setError(errors.Errorf("error encoding inet to pgBinary: %v", v.IPAddr)) 395 } 396 397 case *tree.DEnum: 398 b.writeLengthPrefixedString(v.LogicalRep) 399 400 case *tree.DString: 401 b.writeLengthPrefixedString(string(*v)) 402 403 case *tree.DCollatedString: 404 b.writeLengthPrefixedString(v.Contents) 405 406 case *tree.DTimestamp: 407 b.putInt32(8) 408 b.putInt64(timeToPgBinary(v.Time, nil)) 409 410 case *tree.DTimestampTZ: 411 b.putInt32(8) 412 b.putInt64(timeToPgBinary(v.Time, sessionLoc)) 413 414 case *tree.DDate: 415 b.putInt32(4) 416 b.putInt32(v.PGEpochDays()) 417 418 case *tree.DTime: 419 b.putInt32(8) 420 b.putInt64(int64(*v)) 421 422 case *tree.DTimeTZ: 423 b.putInt32(12) 424 b.putInt64(int64(v.TimeOfDay)) 425 b.putInt32(v.OffsetSecs) 426 427 case *tree.DInterval: 428 b.putInt32(16) 429 b.putInt64(v.Nanos() / int64(time.Microsecond/time.Nanosecond)) 430 b.putInt32(int32(v.Days)) 431 b.putInt32(int32(v.Months)) 432 433 case *tree.DTuple: 434 // TODO(andrei): We shouldn't be allocating a new buffer for every array. 435 subWriter := newWriteBuffer(nil /* bytecount */) 436 // Put the number of datums. 437 subWriter.putInt32(int32(len(v.D))) 438 for _, elem := range v.D { 439 oid := elem.ResolvedType().Oid() 440 subWriter.putInt32(int32(oid)) 441 subWriter.writeBinaryDatum(ctx, elem, sessionLoc, oid) 442 } 443 b.writeLengthPrefixedBuffer(&subWriter.wrapped) 444 445 case *tree.DGeography: 446 b.putInt32(int32(len(v.EWKB()))) 447 b.write(v.EWKB()) 448 449 case *tree.DGeometry: 450 b.putInt32(int32(len(v.EWKB()))) 451 b.write(v.EWKB()) 452 453 case *tree.DArray: 454 if v.ParamTyp.Family() == types.ArrayFamily { 455 b.setError(unimplemented.NewWithIssueDetail(32552, 456 "binenc", "unsupported binary serialization of multidimensional arrays")) 457 return 458 } 459 // TODO(andrei): We shouldn't be allocating a new buffer for every array. 460 subWriter := newWriteBuffer(nil /* bytecount */) 461 // Put the number of dimensions. We currently support 1d arrays only. 462 var ndims int32 = 1 463 if v.Len() == 0 { 464 ndims = 0 465 } 466 subWriter.putInt32(ndims) 467 hasNulls := 0 468 if v.HasNulls { 469 hasNulls = 1 470 } 471 oid := v.ParamTyp.Oid() 472 subWriter.putInt32(int32(hasNulls)) 473 subWriter.putInt32(int32(oid)) 474 if v.Len() > 0 { 475 subWriter.putInt32(int32(v.Len())) 476 // Lower bound, we only support a lower bound of 1. 477 subWriter.putInt32(1) 478 for _, elem := range v.Array { 479 subWriter.writeBinaryDatum(ctx, elem, sessionLoc, oid) 480 } 481 } 482 b.writeLengthPrefixedBuffer(&subWriter.wrapped) 483 case *tree.DJSON: 484 s := v.JSON.String() 485 b.putInt32(int32(len(s) + 1)) 486 // Postgres version number, as of writing, `1` is the only valid value. 487 b.writeByte(1) 488 b.writeString(s) 489 case *tree.DOid: 490 b.putInt32(4) 491 b.putInt32(int32(v.DInt)) 492 default: 493 b.setError(errors.AssertionFailedf("unsupported type %T", d)) 494 } 495 } 496 497 const ( 498 pgTimeFormat = "15:04:05.999999" 499 pgTimeTZFormat = pgTimeFormat + "-07:00" 500 pgDateFormat = "2006-01-02" 501 pgTimeStampFormatNoOffset = pgDateFormat + " " + pgTimeFormat 502 pgTimeStampFormat = pgTimeStampFormatNoOffset + "-07:00" 503 pgTime2400Format = "24:00:00" 504 ) 505 506 // formatTime formats t into a format lib/pq understands, appending to the 507 // provided tmp buffer and reallocating if needed. The function will then return 508 // the resulting buffer. 509 func formatTime(t timeofday.TimeOfDay, tmp []byte) []byte { 510 // time.Time's AppendFormat does not recognize 2400, so special case it accordingly. 511 if t == timeofday.Time2400 { 512 return []byte(pgTime2400Format) 513 } 514 return t.ToTime().AppendFormat(tmp, pgTimeFormat) 515 } 516 517 // formatTimeTZ formats t into a format lib/pq understands, appending to the 518 // provided tmp buffer and reallocating if needed. The function will then return 519 // the resulting buffer. 520 // Note it does not understand the "second" component of the offset as lib/pq 521 // cannot parse it. 522 func formatTimeTZ(t timetz.TimeTZ, tmp []byte) []byte { 523 ret := t.ToTime().AppendFormat(tmp, pgTimeTZFormat) 524 // time.Time's AppendFormat does not recognize 2400, so special case it accordingly. 525 if t.TimeOfDay == timeofday.Time2400 { 526 // It instead reads 00:00:00. Replace that text. 527 var newRet []byte 528 newRet = append(newRet, pgTime2400Format...) 529 newRet = append(newRet, ret[len(pgTime2400Format):]...) 530 ret = newRet 531 } 532 return ret 533 } 534 535 func formatTs(t time.Time, offset *time.Location, tmp []byte) (b []byte) { 536 var format string 537 if offset != nil { 538 format = pgTimeStampFormat 539 } else { 540 format = pgTimeStampFormatNoOffset 541 } 542 return formatTsWithFormat(format, t, offset, tmp) 543 } 544 545 // formatTsWithFormat formats t with an optional offset into a format 546 // lib/pq understands, appending to the provided tmp buffer and 547 // reallocating if needed. The function will then return the resulting 548 // buffer. formatTsWithFormat is mostly cribbed from github.com/lib/pq. 549 func formatTsWithFormat(format string, t time.Time, offset *time.Location, tmp []byte) (b []byte) { 550 // Need to send dates before 0001 A.D. with " BC" suffix, instead of the 551 // minus sign preferred by Go. 552 // Beware, "0000" in ISO is "1 BC", "-0001" is "2 BC" and so on 553 if offset != nil { 554 t = t.In(offset) 555 } 556 557 bc := false 558 if t.Year() <= 0 { 559 // flip year sign, and add 1, e.g: "0" will be "1", and "-10" will be "11" 560 t = t.AddDate((-t.Year())*2+1, 0, 0) 561 bc = true 562 } 563 564 b = t.AppendFormat(tmp, format) 565 if bc { 566 b = append(b, " BC"...) 567 } 568 return b 569 } 570 571 // timeToPgBinary calculates the Postgres binary format for a timestamp. The timestamp 572 // is represented as the number of microseconds between the given time and Jan 1, 2000 573 // (dubbed the PGEpochJDate), stored within an int64. 574 func timeToPgBinary(t time.Time, offset *time.Location) int64 { 575 if offset != nil { 576 t = t.In(offset) 577 } else { 578 t = t.UTC() 579 } 580 return duration.DiffMicros(t, pgwirebase.PGEpochJDate) 581 }