result_binary.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. package pgserver
  2. import (
  3. "encoding/binary"
  4. "errors"
  5. "fmt"
  6. "math"
  7. "strconv"
  8. "strings"
  9. "github.com/danfragoso/pizzasql-next/pkg/executor"
  10. )
  11. // Binary result encoding.
  12. //
  13. // DataRow values are normally sent in the PostgreSQL text format. A client that
  14. // asks for a binary result column (Bind result format code 1) gets the same
  15. // value in PostgreSQL's binary representation for the OID RowDescription
  16. // reports. Values are appended directly into the DataRow buffer, and a value
  17. // that cannot be represented fails the row before it is written.
  18. //
  19. // The supported OIDs are the ones this engine reports (see getOIDForType) plus
  20. // the array element types that appear in binary array headers. Types without a
  21. // binary representation here (timestamptz 1184, date 1082) are rejected at
  22. // execution time with 0A000 rather than being sent as text.
  23. // canEncodeBinaryResult reports whether the wire layer can encode a result
  24. // column of this OID in the binary format.
  25. func canEncodeBinaryResult(oid int32) bool {
  26. switch oid {
  27. case 16, // bool
  28. 17, // bytea
  29. 20, // int8
  30. 21, // int2
  31. 23, // int4
  32. 25, // text
  33. 19, // name
  34. 26, // oid
  35. 700, // float4
  36. 701, // float8
  37. 1043, // varchar
  38. 1700, // numeric
  39. 2205, // regclass
  40. 2206, // regtype
  41. 3802: // jsonb
  42. return true
  43. }
  44. _, ok := binaryResultElementOID(oid)
  45. return ok
  46. }
  47. // binaryResultElementOID maps a one-dimensional array OID to the element OID its
  48. // binary envelope must carry.
  49. func binaryResultElementOID(arrayOID int32) (int32, bool) {
  50. switch arrayOID {
  51. case 1000: // bool[]
  52. return 16, true
  53. case 1005: // int2[]
  54. return 21, true
  55. case 1007: // int4[]
  56. return 23, true
  57. case 1009: // text[]
  58. return 25, true
  59. case 1015: // varchar[]
  60. return 1043, true
  61. case 1016: // int8[]
  62. return 20, true
  63. case 1021: // float4[]
  64. return 700, true
  65. case 1022: // float8[]
  66. return 701, true
  67. case 1231: // numeric[]
  68. return 1700, true
  69. default:
  70. return 0, false
  71. }
  72. }
  73. // binaryResultEncodeError reports a value the requested binary format cannot
  74. // represent, such as text left in an INTEGER column by SQLite affinity. The
  75. // connection reports it as invalid text input and sends no further rows.
  76. type binaryResultEncodeError struct {
  77. err error
  78. }
  79. func (e *binaryResultEncodeError) Error() string { return e.err.Error() }
  80. func (e *binaryResultEncodeError) Unwrap() error { return e.err }
  81. func binaryEncodeErrorf(format string, args ...interface{}) error {
  82. return &binaryResultEncodeError{err: fmt.Errorf(format, args...)}
  83. }
  84. // failBinaryEncode reports a value the requested binary format cannot represent.
  85. // Rows already written stay written (an error discards the rest of the result),
  86. // but the failing DataRow is never emitted.
  87. func (c *Connection) failBinaryEncode(err error) error {
  88. var encodeErr *binaryResultEncodeError
  89. if errors.As(err, &encodeErr) {
  90. return c.failExtended(ErrCodeInvalidText, err)
  91. }
  92. return err
  93. }
  94. // appendBinaryResultValue appends the PostgreSQL binary representation of value
  95. // for oid.
  96. func appendBinaryResultValue(dst []byte, oid int32, value interface{}) ([]byte, error) {
  97. if elementOID, ok := binaryResultElementOID(oid); ok {
  98. return appendBinaryArray(dst, elementOID, value)
  99. }
  100. switch oid {
  101. case 16: // bool
  102. flag, err := binaryBool(value)
  103. if err != nil {
  104. return dst, err
  105. }
  106. if flag {
  107. return append(dst, 1), nil
  108. }
  109. return append(dst, 0), nil
  110. case 17: // bytea
  111. return append(dst, binaryBytes(value)...), nil
  112. case 20, 21, 23: // int8, int2, int4
  113. width := 8
  114. switch oid {
  115. case 21:
  116. width = 2
  117. case 23:
  118. width = 4
  119. }
  120. n, err := binaryInt64(value, oid)
  121. if err != nil {
  122. return dst, err
  123. }
  124. switch width {
  125. case 2:
  126. return append(dst, byte(n>>8), byte(n)), nil
  127. case 4:
  128. return append(dst, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)), nil
  129. default:
  130. return appendUint64(dst, uint64(n)), nil
  131. }
  132. case 19: // name
  133. if reg, ok := value.(executor.RegOID); ok {
  134. return append(dst, reg.String()...), nil
  135. }
  136. return append(dst, textValueString(value)...), nil
  137. case 26, 2205, 2206: // oid, regclass, regtype
  138. n, err := catalogOIDBinaryValue(value)
  139. if err != nil {
  140. return dst, err
  141. }
  142. return append(dst, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)), nil
  143. case 25, 1043: // text, varchar
  144. return append(dst, textValueString(value)...), nil
  145. case 700: // float4
  146. f, err := binaryFloat64(value)
  147. if err != nil {
  148. return dst, err
  149. }
  150. // A value beyond float4's range would otherwise convert to ±Inf and be
  151. // decoded as infinity instead of the stored value. NaN compares false
  152. // here and stays NaN, which float4 can represent.
  153. if math.Abs(f) > math.MaxFloat32 {
  154. return dst, binaryEncodeErrorf("value %v does not fit float4", f)
  155. }
  156. bits := math.Float32bits(float32(f))
  157. return append(dst, byte(bits>>24), byte(bits>>16), byte(bits>>8), byte(bits)), nil
  158. case 701: // float8
  159. f, err := binaryFloat64(value)
  160. if err != nil {
  161. return dst, err
  162. }
  163. return appendUint64(dst, math.Float64bits(f)), nil
  164. case 1700: // numeric
  165. return appendNumericValue(dst, value)
  166. case 3802: // jsonb: version byte plus canonical JSON text
  167. dst = append(dst, 1)
  168. return append(dst, binaryJSONText(value)...), nil
  169. default:
  170. return dst, binaryEncodeErrorf("binary result format is not supported for OID %d", oid)
  171. }
  172. }
  173. // appendBinaryArray appends a one-dimensional binary array. The stored value is
  174. // the canonical PostgreSQL array text, so parsing it yields the same elements
  175. // the text format carries; NULL elements keep their -1 length and an empty
  176. // array has no dimension header, as array_recv expects.
  177. func appendBinaryArray(dst []byte, elementOID int32, value interface{}) ([]byte, error) {
  178. text := binaryJSONText(value)
  179. elements, err := executor.ParseArrayText(text)
  180. if err != nil {
  181. return dst, binaryEncodeErrorf("invalid array value %q: %v", text, err)
  182. }
  183. hasNull := false
  184. for _, element := range elements {
  185. if element == nil {
  186. hasNull = true
  187. break
  188. }
  189. }
  190. dimensions := int32(1)
  191. if len(elements) == 0 {
  192. dimensions = 0
  193. }
  194. dst = appendUint32(dst, uint32(dimensions))
  195. if hasNull {
  196. dst = appendUint32(dst, 1)
  197. } else {
  198. dst = appendUint32(dst, 0)
  199. }
  200. dst = appendUint32(dst, uint32(elementOID))
  201. if dimensions == 0 {
  202. return dst, nil
  203. }
  204. dst = appendUint32(dst, uint32(len(elements)))
  205. dst = appendUint32(dst, 1) // lower bound
  206. for _, element := range elements {
  207. if element == nil {
  208. dst = appendUint32(dst, math.MaxUint32) // -1: NULL element
  209. continue
  210. }
  211. start := len(dst)
  212. dst = appendUint32(dst, 0)
  213. dst, err = appendBinaryResultValue(dst, elementOID, *element)
  214. if err != nil {
  215. return dst, err
  216. }
  217. binary.BigEndian.PutUint32(dst[start:start+4], uint32(len(dst)-start-4))
  218. }
  219. return dst, nil
  220. }
  221. func appendUint32(dst []byte, n uint32) []byte {
  222. return append(dst, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
  223. }
  224. func appendUint64(dst []byte, n uint64) []byte {
  225. return append(dst, byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
  226. }
  227. // binaryInt64 converts an engine value to an integer result of the given OID.
  228. // Text is accepted because SQLite affinity can leave a text value in an integer
  229. // column; a value that does not fit the reported OID is an encoding error
  230. // instead of a silently truncated one.
  231. // catalogOIDBinaryValue encodes an oid/regclass/regtype value. A RegOID carries
  232. // its numeric OID separately from its display name.
  233. func catalogOIDBinaryValue(value interface{}) (int64, error) {
  234. if reg, ok := value.(executor.RegOID); ok {
  235. return binaryInt64(reg.OID(), 23)
  236. }
  237. return binaryInt64(value, 23)
  238. }
  239. func binaryInt64(value interface{}, oid int32) (int64, error) {
  240. var n int64
  241. switch v := value.(type) {
  242. case int64:
  243. n = v
  244. case int:
  245. n = int64(v)
  246. case int32:
  247. n = int64(v)
  248. case int16:
  249. n = int64(v)
  250. case int8:
  251. n = int64(v)
  252. case float64:
  253. if math.Trunc(v) != v || v < math.MinInt64 || v >= math.MaxInt64 {
  254. return 0, binaryEncodeErrorf("value %v is not an integer", v)
  255. }
  256. n = int64(v)
  257. case string:
  258. parsed, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
  259. if err != nil {
  260. return 0, binaryEncodeErrorf("value %q is not an integer", v)
  261. }
  262. n = parsed
  263. case []byte:
  264. parsed, err := strconv.ParseInt(strings.TrimSpace(string(v)), 10, 64)
  265. if err != nil {
  266. return 0, binaryEncodeErrorf("value %q is not an integer", v)
  267. }
  268. n = parsed
  269. default:
  270. return 0, binaryEncodeErrorf("value of type %T is not an integer", value)
  271. }
  272. switch oid {
  273. case 21:
  274. if n < math.MinInt16 || n > math.MaxInt16 {
  275. return 0, binaryEncodeErrorf("value %d does not fit int2", n)
  276. }
  277. case 23:
  278. if n < math.MinInt32 || n > math.MaxInt32 {
  279. return 0, binaryEncodeErrorf("value %d does not fit int4", n)
  280. }
  281. }
  282. return n, nil
  283. }
  284. // binaryFloat64 converts an engine value to a floating-point result.
  285. func binaryFloat64(value interface{}) (float64, error) {
  286. switch v := value.(type) {
  287. case float64:
  288. return v, nil
  289. case float32:
  290. return float64(v), nil
  291. case int64:
  292. return float64(v), nil
  293. case int:
  294. return float64(v), nil
  295. case int32:
  296. return float64(v), nil
  297. case int16:
  298. return float64(v), nil
  299. case string:
  300. parsed, err := strconv.ParseFloat(strings.TrimSpace(v), 64)
  301. if err != nil {
  302. return 0, binaryEncodeErrorf("value %q is not a floating-point number", v)
  303. }
  304. return parsed, nil
  305. case []byte:
  306. parsed, err := strconv.ParseFloat(strings.TrimSpace(string(v)), 64)
  307. if err != nil {
  308. return 0, binaryEncodeErrorf("value %q is not a floating-point number", v)
  309. }
  310. return parsed, nil
  311. default:
  312. return 0, binaryEncodeErrorf("value of type %T is not a floating-point number", value)
  313. }
  314. }
  315. // binaryBool converts an engine value to a boolean result. Boolean columns hold
  316. // int64 0/1 from storage, bool from expressions, and text when the inserted
  317. // literal did not convert.
  318. func binaryBool(value interface{}) (bool, error) {
  319. switch v := value.(type) {
  320. case bool:
  321. return v, nil
  322. case int64:
  323. return v != 0, nil
  324. case int:
  325. return v != 0, nil
  326. case int32:
  327. return v != 0, nil
  328. case int16:
  329. return v != 0, nil
  330. case float64:
  331. return v != 0, nil
  332. case string:
  333. return boolText(v)
  334. case []byte:
  335. return boolText(string(v))
  336. default:
  337. return false, binaryEncodeErrorf("value of type %T is not a boolean", value)
  338. }
  339. }
  340. func boolText(text string) (bool, error) {
  341. switch strings.ToLower(strings.TrimSpace(text)) {
  342. case "t", "true", "1", "y", "yes", "on":
  343. return true, nil
  344. case "f", "false", "0", "n", "no", "off":
  345. return false, nil
  346. default:
  347. return false, binaryEncodeErrorf("value %q is not a boolean", text)
  348. }
  349. }
  350. // binaryBytes returns the bytes a binary bytea result carries. A non-[]byte
  351. // value uses the same text the bytea text format would send, which is the
  352. // escape-format interpretation of the value's spelling.
  353. func binaryBytes(value interface{}) []byte {
  354. if b, ok := value.([]byte); ok {
  355. return b
  356. }
  357. return []byte(textValueString(value))
  358. }
  359. // binaryJSONText returns the JSON text a jsonb result carries.
  360. func binaryJSONText(value interface{}) string {
  361. switch v := value.(type) {
  362. case string:
  363. return v
  364. case []byte:
  365. return string(v)
  366. default:
  367. return textValueString(value)
  368. }
  369. }
  370. // appendNumericValue appends a PostgreSQL binary numeric value. Numeric values
  371. // are stored as exact decimal text, so they are encoded digit by digit into
  372. // base-10000 groups; converting through float64 would round digits above 2^53
  373. // and lose the display scale (1.10 would become 1.1).
  374. func appendNumericValue(dst []byte, value interface{}) ([]byte, error) {
  375. var text string
  376. switch v := value.(type) {
  377. case string:
  378. text = v
  379. case []byte:
  380. text = string(v)
  381. case int64:
  382. text = strconv.FormatInt(v, 10)
  383. case int:
  384. text = strconv.FormatInt(int64(v), 10)
  385. case float64:
  386. text = strconv.FormatFloat(v, 'g', -1, 64)
  387. default:
  388. return dst, binaryEncodeErrorf("value of type %T is not numeric", value)
  389. }
  390. return appendNumericText(dst, text)
  391. }
  392. // appendNumericText encodes exact decimal text as a PostgreSQL numeric: ndigits,
  393. // weight, sign, dscale, then base-10000 groups. Exponents are applied to the
  394. // decimal point, trailing fraction zeros only affect dscale, and leading zero
  395. // groups are dropped while adjusting weight.
  396. func appendNumericText(dst []byte, text string) ([]byte, error) {
  397. invalid := func() ([]byte, error) {
  398. return dst, binaryEncodeErrorf("invalid numeric value %q", text)
  399. }
  400. s := strings.TrimSpace(text)
  401. if s == "" {
  402. return invalid()
  403. }
  404. negative := false
  405. if s[0] == '+' || s[0] == '-' {
  406. negative = s[0] == '-'
  407. s = s[1:]
  408. }
  409. if s == "" {
  410. return invalid()
  411. }
  412. exponent := 0
  413. if index := strings.IndexAny(s, "eE"); index >= 0 {
  414. parsed, err := strconv.Atoi(s[index+1:])
  415. if err != nil {
  416. return invalid()
  417. }
  418. if parsed > math.MaxInt16*4 || parsed < math.MinInt16*4 {
  419. return dst, binaryEncodeErrorf("numeric exponent %d is out of range", parsed)
  420. }
  421. exponent = parsed
  422. s = s[:index]
  423. }
  424. point := strings.IndexByte(s, '.')
  425. var digits string
  426. if point < 0 {
  427. point = len(s)
  428. digits = s
  429. } else {
  430. digits = s[:point] + s[point+1:]
  431. }
  432. if digits == "" {
  433. return invalid()
  434. }
  435. for i := 0; i < len(digits); i++ {
  436. if digits[i] < '0' || digits[i] > '9' {
  437. return invalid()
  438. }
  439. }
  440. point += exponent
  441. if point > math.MaxInt16*4 || point < math.MinInt16*4 {
  442. return dst, binaryEncodeErrorf("numeric exponent is out of range in %q", text)
  443. }
  444. // The display scale is the number of fraction digits after the exponent,
  445. // including zeros this encoder drops from the base-10000 groups (1.10 keeps
  446. // dscale 2).
  447. dscale := len(digits) - point
  448. if dscale < 0 {
  449. dscale = 0
  450. }
  451. if dscale > math.MaxInt16 {
  452. return dst, binaryEncodeErrorf("numeric scale %d is out of range", dscale)
  453. }
  454. integerDigits := ""
  455. if point >= len(digits) {
  456. integerDigits = digits + strings.Repeat("0", point-len(digits))
  457. } else if point > 0 {
  458. integerDigits = digits[:point]
  459. }
  460. fractionDigits := ""
  461. if point < 0 {
  462. fractionDigits = strings.Repeat("0", -point) + digits
  463. } else if point < len(digits) {
  464. fractionDigits = digits[point:]
  465. }
  466. var groups []uint16
  467. weight := 0
  468. if trimmed := strings.TrimLeft(integerDigits, "0"); trimmed != "" {
  469. padding := (4 - len(trimmed)%4) % 4
  470. padded := strings.Repeat("0", padding) + trimmed
  471. weight = len(padded)/4 - 1
  472. for i := 0; i < len(padded); i += 4 {
  473. groups = append(groups, uint16(decimalGroup(padded[i:i+4])))
  474. }
  475. }
  476. if trimmed := strings.TrimRight(fractionDigits, "0"); trimmed != "" {
  477. padding := (4 - len(trimmed)%4) % 4
  478. padded := trimmed + strings.Repeat("0", padding)
  479. if len(groups) == 0 {
  480. weight = -1
  481. }
  482. for i := 0; i < len(padded); i += 4 {
  483. groups = append(groups, uint16(decimalGroup(padded[i:i+4])))
  484. }
  485. }
  486. leading := 0
  487. for leading < len(groups) && groups[leading] == 0 {
  488. leading++
  489. }
  490. if leading == len(groups) {
  491. groups = nil
  492. weight = 0
  493. } else {
  494. groups = groups[leading:]
  495. weight -= leading
  496. }
  497. for len(groups) > 0 && groups[len(groups)-1] == 0 {
  498. groups = groups[:len(groups)-1]
  499. }
  500. if weight < math.MinInt16 || weight > math.MaxInt16 {
  501. return dst, binaryEncodeErrorf("numeric weight %d is out of range", weight)
  502. }
  503. if len(groups) > math.MaxInt16 {
  504. return dst, binaryEncodeErrorf("numeric has %d base-10000 groups, out of range", len(groups))
  505. }
  506. dst = append(dst, byte(len(groups)>>8), byte(len(groups)))
  507. dst = append(dst, byte(weight>>8), byte(weight))
  508. sign := 0
  509. if negative && len(groups) > 0 {
  510. sign = 0x4000
  511. }
  512. dst = append(dst, byte(sign>>8), byte(sign))
  513. dst = append(dst, byte(dscale>>8), byte(dscale))
  514. for _, group := range groups {
  515. dst = append(dst, byte(group>>8), byte(group))
  516. }
  517. return dst, nil
  518. }
  519. // decimalGroup parses four decimal digits, which are always in 0000-9999.
  520. func decimalGroup(digits string) int {
  521. n := 0
  522. for i := 0; i < len(digits); i++ {
  523. n = n*10 + int(digits[i]-'0')
  524. }
  525. return n
  526. }