_site/cover/pgmp_data_row.COVER.html

1 %% Copyright (c) 2022 Peter Morgan <peter.james.morgan@gmail.com>
2 %%
3 %% Licensed under the Apache License, Version 2.0 (the "License");
4 %% you may not use this file except in compliance with the License.
5 %% You may obtain a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing, software
10 %% distributed under the License is distributed on an "AS IS" BASIS,
11 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 %% See the License for the specific language governing permissions and
13 %% limitations under the License.
14
15
16 -module(pgmp_data_row).
17
18
19 -define(NBASE,10_000).
20 -export([decode/3]).
21 -export([decode/5]).
22 -export([encode/3]).
23 -export([encode/5]).
24 -export([numeric_encode/1]).
25 -export_type([decoded/0]).
26 -import(pgmp_codec, [marshal/2]).
27 -import(pgmp_codec, [size_exclusive/1]).
28 -include_lib("kernel/include/logger.hrl").
29
30
31 -type format() :: text | binary.
32 -type type_format() :: #{format := format(), type_oid := pgmp:oid()}.
33 -type encoded() :: {type_format(), binary() | null}.
34
35 -type inet32() :: {pgmp:uint8(), pgmp:uint8(), pgmp:uint8(), pgmp:uint8()}.
36
37 -type inet128() :: {pgmp:uint16(),
38 pgmp:uint16(),
39 pgmp:uint16(),
40 pgmp:uint16(),
41 pgmp:uint16(),
42 pgmp:uint16(),
43 pgmp:uint16(),
44 pgmp:uint16()}.
45
46 -type decoded() :: boolean()
47 | integer()
48 | bitstring()
49 | binary()
50 | [decoded()]
51 | calendar:date()
52 | calendar:time()
53 | calendar:datetime()
54 | '-Infinity'
55 | 'Infinity'
56 | 'NaN'
57 | float()
58 | inet32()
59 | inet128()
60 | pgmp_geo:point()
61 | pgmp_geo:path()
62 | pgmp_geo:polygon()
63 | pgmp_geo:circle()
64 | pgmp_geo:line()
65 | pgmp_geo:lseg()
66 | pgmp_geo:box().
67
68
69 %% -spec decode(pgmp:parameters(), [encoded()]) -> [decoded()].
70
71 %% decode(Parameters, TypeValue) ->
72 %% ?FUNCTION_NAME(Parameters, TypeValue, pgmp_types:cache()).
73
74
75 -spec decode(pgmp:parameters(), [encoded()], pgmp_types:cache()) -> [decoded()].
76
77 decode(Parameters, TypeValue, Types) ->
78 10350 ?LOG_DEBUG(#{parameters => Parameters, type_value => TypeValue}),
79 10350 ?FUNCTION_NAME(Parameters, TypeValue, Types, []).
80
81
82 -spec decode(pgmp:parameters(), [encoded()], pgmp_types:cache(), [decoded()]) -> [decoded()].
83
84 decode(Parameters, [{_, null} | T], Types, A) ->
85 18379 ?FUNCTION_NAME(Parameters, T, Types, [null | A]);
86
87 decode(Parameters,
88 [{#{format := Format, type_oid := OID}, Value} | T],
89 Types,
90 A) ->
91 182596 case maps:find(OID, Types) of
92 {ok, Type} ->
93 4806 ?FUNCTION_NAME(
94 Parameters,
95 T,
96 Types,
97 [?FUNCTION_NAME(Parameters,
98 Format,
99 Types,
100 Type,
101 Value) | A]);
102
103 error ->
104 177790 ?FUNCTION_NAME(Parameters,
105 T,
106 Types,
107 [Value | A])
108 end;
109
110 decode(_, [], _, A) ->
111 10350 lists:reverse(A).
112
113
114 decode(_, binary, _, #{<<"typname">> := <<"bool">>}, <<1>>) ->
115 62 true;
116
117 decode(_, text, _, #{<<"typname">> := <<"bool">>}, <<"t">>) ->
118 1 true;
119
120 decode(_, binary, _, #{<<"typname">> := <<"bool">>}, <<0>>) ->
121 40 false;
122
123 decode(_, text, _, #{<<"typname">> := <<"bool">>}, <<"f">>) ->
124 1 false;
125
126 decode(_,
127 text,
128 _,
129 #{<<"typname">> := Name},
130 Value) when Name == <<"oid">>;
131 Name == <<"regproc">>;
132 Name == <<"regprocedure">>;
133 Name == <<"regoper">>;
134 Name == <<"regoperator">>;
135 Name == <<"regclass">>;
136 Name == <<"regtype">>;
137 Name == <<"regconfig">>;
138 Name == <<"regdictionary">> ->
139 1 binary_to_integer(Value);
140
141 decode(_, text, _, #{<<"typname">> := <<"money">>}, Value) ->
142
:-(
binary_to_integer(Value);
143
144 decode(_,
145 binary,
146 _,
147 #{<<"typname">> := Name},
148 <<Value:32>>) when Name == <<"oid">>;
149 Name == <<"regproc">>;
150 Name == <<"regprocedure">>;
151 Name == <<"regoper">>;
152 Name == <<"regoperator">>;
153 Name == <<"regclass">>;
154 Name == <<"regtype">>;
155 Name == <<"regconfig">>;
156 Name == <<"regdictionary">> ->
157 903 Value;
158
159 decode(_,
160 binary,
161 _,
162 #{<<"typname">> := Name},
163 <<Length:32, Data:Length/bits>>)
164 when Length rem 8 == 0,
165 Name == <<"bit">>;
166 Name == <<"varbit">> ->
167 10 Data;
168
169 decode(_,
170 binary,
171 _,
172 #{<<"typname">> := Name},
173 <<Length:32, Data:Length/bits, 0:(8 - (Length rem 8))>>)
174 when Name == <<"bit">>;
175 Name == <<"varbit">> ->
176 91 Data;
177
178 decode(_,
179 text,
180 _,
181 #{<<"typname">> := <<"bit">>},
182 Encoded) ->
183 1 erlang:list_to_bitstring(
184 3 [<<(binary_to_integer(I)):1>> || <<I:1/bytes>> <= Encoded]);
185
186 decode(_,
187 text,
188 _,
189 #{<<"typname">> := Name},
190 Encoded)
191 when Name == <<"int2">>;
192 Name == <<"int4">>;
193 Name == <<"int8">> ->
194 11 binary_to_integer(Encoded);
195
196 decode(_,
197 binary,
198 _,
199 #{<<"typname">> := <<"uuid">>},
200 <<TimeLow:32, TimeMid:16, TimeHi:16, Clock:16, Node:48>>) ->
201 101 iolist_to_binary(
202 io_lib:format(
203 "~8.16.0b-~4.16.0b-~4.16.0b-~4.16.0b-~12.16.0b",
204 [TimeLow, TimeMid, TimeHi, Clock, Node]));
205
206 decode(_,
207 text,
208 _,
209 #{<<"typname">> := <<"uuid">>},
210 <<_:8/bytes,
211 "-",
212 _:4/bytes,
213 "-",
214 _:4/bytes,
215 "-",
216 _:4/bytes,
217 "-",
218 _:12/bytes>> = UUID) ->
219 1 UUID;
220
221 decode(Parameters,
222 binary = Format,
223 _,
224 #{<<"typname">> := <<"jsonb">> = Type},
225 <<1:8, Encoded/bytes>>) ->
226 2 ?LOG_DEBUG(#{parameters => Parameters, format => Format, type => Type, encoded => Encoded}),
227 2 (pgmp_config:codec(binary_to_atom(Type))):?FUNCTION_NAME(Encoded);
228
229 decode(Parameters,
230 Format,
231 _,
232 #{<<"typname">> := Type}, Encoded)
233 when Type == <<"xml">>;
234 Type == <<"jsonb">>;
235 Type == <<"json">> ->
236 7 ?LOG_DEBUG(#{parameters => Parameters, format => Format, type => Type, encoded => Encoded}),
237 7 (pgmp_config:codec(binary_to_atom(Type))):?FUNCTION_NAME(Encoded);
238
239 %% src/backend/utils/adt/arrayfuncs.c#array_recv
240 decode(Parameters,
241 Format,
242 Types,
243 #{<<"typreceive">> := TypeReceive} = Type,
244 Data) when TypeReceive == <<"array_recv">>;
245 TypeReceive == <<"int2vectorrecv">>;
246 TypeReceive == <<"oidvectorrecv">> ->
247 116 array_recv(Parameters, Format, Types, Type, Data);
248
249 decode(_,
250 text,
251 _,
252 #{<<"typname">> := <<"float", _/bytes>>},
253 Encoded)
254 when Encoded == <<"NaN">>;
255 Encoded == <<"Infinity">>;
256 Encoded == <<"-Infinity">> ->
257
:-(
binary_to_atom(Encoded);
258
259 decode(_, text, _, #{<<"typname">> := <<"float", _/bytes>>}, Encoded) ->
260 1 numeric(Encoded);
261
262 decode(_, text, _, #{<<"typname">> := <<"numeric">>}, Encoded) ->
263
:-(
numeric(Encoded);
264
265
266 decode(_,
267 binary,
268 _,
269 #{<<"typname">> := <<"numeric">>},
270 <<Length:16,
271 Weight:16/signed,
272 _Sign:16,
273 _DScale:16,
274 Digits:Length/binary-unit:16>> = Encoded) ->
275 105 lists:foldl(
276 numeric_decode(Encoded),
277 0,
278 lists:zip(
279 103 [Digit || <<Digit:16>> <= Digits],
280 lists:seq(Weight, Weight - Length + 1, -1)));
281
282 decode(_, text, _, #{<<"typname">> := <<"date">>}, <<Ye:4/bytes, "-", Mo:2/bytes, "-", Da:2/bytes>>) ->
283 1 triple(Ye, Mo, Da);
284
285 decode(_, binary, _, #{<<"typname">> := <<"date">>}, <<Days:32/signed>>) ->
286 103 calendar:gregorian_days_to_date(
287 calendar:date_to_gregorian_days(pgmp_calendar:epoch_date(pg)) + Days);
288
289 decode(_, text, _, #{<<"typname">> := <<"time">>}, <<Ho:2/bytes, ":", Mi:2/bytes, ":", Se:2/bytes>>) ->
290 1 triple(Ho, Mi, Se);
291
292 decode(_, binary, _, #{<<"typname">> := <<"time">>}, <<Time:64>>) ->
293 100 calendar:seconds_to_time(erlang:convert_time_unit(Time, microsecond, second));
294
295 decode(#{<<"integer_datetimes">> := <<"on">>},
296 text,
297 _,
298 #{<<"typname">> := Name},
299 <<Ye:4/bytes,
300 "-",
301 Mo:2/bytes,
302 "-",
303 Da:2/bytes,
304 " ",
305 Ho:2/bytes,
306 ":",
307 Mi:2/bytes,
308 ":",
309 Se:2/bytes,
310 _/bytes>>)
311 when Name == <<"timestamp">>;
312 Name == <<"timestamptz">> ->
313 1 {triple(Ye, Mo, Da), triple(Ho, Mi, Se)};
314
315 decode(_, binary, _, #{<<"typname">> := Name}, <<Encoded:64/signed>>)
316 when Name == <<"timestamp">>;
317 Name == <<"timestamptz">> ->
318 200 pgmp_calendar:decode(Encoded);
319
320 decode(_, binary, _, #{<<"typname">> := <<"money">>}, <<Decoded:64/signed>>) ->
321 100 Decoded;
322
323 decode(_, binary, _, #{<<"typname">> := <<"int", R/bytes>>}, Encoded) ->
324 1801 Size = binary_to_integer(R),
325 1801 <<Decoded:Size/signed-unit:8>> = Encoded,
326 1801 Decoded;
327
328 decode(_,
329 binary,
330 _,
331 #{<<"typname">> := <<"float", _/bytes>>},
332 <<1:1, ((1 bsl 8) - 1):8, 0:23>>) ->
333
:-(
'-Infinity';
334
335 decode(_,
336 binary,
337 _,
338 #{<<"typname">> := <<"float", _/bytes>>},
339 <<0:1, ((1 bsl 8) - 1):8, 0:23>>) ->
340
:-(
'Infinity';
341
342 decode(_,
343 binary,
344 _,
345 #{<<"typname">> := <<"float", _/bytes>>},
346 <<_:1, ((1 bsl 8) - 1):8, (1 bsl 22):23>>) ->
347
:-(
'NaN';
348
349 decode(_,
350 binary,
351 _,
352 #{<<"typname">> := <<"float", _/bytes>>},
353 <<1:1, ((1 bsl 11) - 1):11, 0:52>>) ->
354
:-(
'-Infinity';
355
356 decode(_,
357 binary,
358 _,
359 #{<<"typname">> := <<"float", _/bytes>>},
360 <<0:1, ((1 bsl 11) - 1):11, 0:52>>) ->
361
:-(
'Infinity';
362
363 decode(_,
364 binary,
365 _,
366 #{<<"typname">> := <<"float", _/bytes>>},
367 <<_:1, ((1 bsl 11) - 1):11, (1 bsl 51):52>>) ->
368
:-(
'NaN';
369
370 decode(_,
371 binary,
372 _,
373 #{<<"typname">> := <<"float", R/bytes>>},
374 Encoded) ->
375 200 Size = binary_to_integer(R),
376 200 <<Decoded:Size/signed-float-unit:8>> = Encoded,
377 200 precision(Decoded, <<"float", R/bytes>>);
378
379 decode(_,
380 _,
381 _,
382 #{<<"typname">> := <<"bpchar">>},
383 <<Encoded/bytes>>) ->
384 2 Encoded;
385
386 decode(#{<<"client_encoding">> := <<"UTF8">>},
387 _,
388 _,
389 #{<<"typname">> := Type},
390 <<Encoded/bytes>>) when Type == <<"varchar">>;
391 Type == <<"char">>;
392 Type == <<"name">>;
393 Type == <<"regtype">>;
394 Type == <<"text">> ->
395 716 unicode:characters_to_binary(Encoded);
396
397 decode(#{<<"client_encoding">> := <<"SQL_ASCII">>},
398 _,
399 _,
400 #{<<"typname">> := Type},
401 <<Encoded/bytes>>) when Type == <<"varchar">>;
402 Type == <<"char">>;
403 Type == <<"name">>;
404 Type == <<"regtype">>;
405 Type == <<"text">> ->
406
:-(
unicode:characters_to_binary(Encoded, latin1);
407
408 decode(_,
409 text,
410 _,
411 #{<<"typname">> := <<"bytea">>},
412 <<"\\x", Encoded/bytes>>) ->
413 2 list_to_binary([binary_to_integer(X, 16) || <<X:2/bytes>> <= Encoded]);
414
415 decode(_,
416 binary,
417 _,
418 #{<<"typname">> := <<"bytea">>},
419 <<Encoded/bytes>>) ->
420 101 Encoded;
421
422 decode(_,
423 binary,
424 _,
425 #{<<"typname">> := <<"tsvector">>},
426 Encoded) ->
427 3 pgmp_tsvector:decode(Encoded);
428
429 decode(_,
430 binary,
431 _,
432 #{<<"typname">> := <<"tsquery">>},
433 Encoded) ->
434 1 pgmp_tsquery:decode(Encoded);
435
436 decode(_,
437 binary,
438 _,
439 #{<<"typname">> := <<"macaddr8">>},
440 <<Address:8/bytes>>) ->
441 100 Address;
442
443 decode(_,
444 binary,
445 _,
446 #{<<"typname">> := <<"macaddr">>},
447 <<Address:6/bytes>>) ->
448 100 Address;
449
450 decode(_,
451 text,
452 _,
453 #{<<"typname">> := <<"inet">>},
454 Encoded) ->
455
:-(
{ok, Decoded} = inet:parse_address(binary_to_list(Encoded)),
456
:-(
Decoded;
457
458 decode(_,
459 binary,
460 _,
461 #{<<"typname">> := <<"inet">>},
462 <<2:8, 32:8, 0:8, Size:8, Encoded:Size/bytes>>) ->
463 51 list_to_tuple([Octet || <<Octet:8>> <= Encoded]);
464
465 decode(_,
466 binary,
467 _,
468 #{<<"typname">> := <<"inet">>},
469 <<3:8, 128:8, 0:8, Size:8, Encoded:Size/bytes>>) ->
470 49 list_to_tuple([Component || <<Component:16>> <= Encoded]);
471
472 decode(_,
473 binary,
474 _,
475 #{<<"typname">> := <<"point">>},
476 <<X:8/signed-float-unit:8, Y:8/signed-float-unit:8>>) ->
477 100 #{x => X, y => Y};
478
479 decode(_,
480 binary,
481 _,
482 #{<<"typname">> := <<"path">>},
483 <<0:8, _:32, Remainder/bytes>>) ->
484 49 #{path => open,
485 356 points => [#{x => X,
486 y => Y} || <<X:8/signed-float-unit:8,
487 49 Y:8/signed-float-unit:8>> <= Remainder]};
488
489 decode(_,
490 binary,
491 _,
492 #{<<"typname">> := <<"path">>},
493 <<1:8, _:32, Remainder/bytes>>) ->
494 51 #{path => closed,
495 657 points => [#{x => X,
496 y => Y} || <<X:8/signed-float-unit:8,
497 51 Y:8/signed-float-unit:8>> <= Remainder]};
498
499 decode(_,
500 binary,
501 _,
502 #{<<"typname">> := <<"polygon">>},
503 <<_:32, Remainder/bytes>>) ->
504 100 [#{x => X,
505 y => Y} || <<X:8/signed-float-unit:8,
506 100 Y:8/signed-float-unit:8>> <= Remainder];
507
508 decode(_,
509 binary,
510 _,
511 #{<<"typname">> := <<"circle">>},
512 <<X:8/signed-float-unit:8,
513 Y:8/signed-float-unit:8,
514 Radius:8/signed-float-unit:8>>) ->
515 100 #{x => X, y => Y, radius => Radius};
516
517 decode(_,
518 binary,
519 _,
520 #{<<"typname">> := Type},
521 <<X1:8/signed-float-unit:8, Y1:8/signed-float-unit:8,
522 X2:8/signed-float-unit:8, Y2:8/signed-float-unit:8>>)
523 when Type == <<"lseg">>;
524 Type == <<"box">> ->
525 200 {#{x => X1, y => Y1}, #{x => X2, y => Y2}};
526
527 decode(_,
528 binary,
529 _,
530 #{<<"typname">> := <<"line">>},
531 <<A:8/signed-float-unit:8,
532 B:8/signed-float-unit:8,
533 C:8/signed-float-unit:8>>) ->
534 %% Lines are represented by the linear equation Ax + By + C = 0,
535 %% where A and B are not both zero.
536 100 #{a => A, b => B, c => C};
537
538 decode(_,
539 binary,
540 _,
541 #{<<"typname">> := <<"geometry">>},
542 WKB) ->
543
:-(
binary:encode_hex(WKB);
544
545 decode(_,
546 text,
547 _,
548 #{<<"typname">> := <<"raster">>},
549 Raster) ->
550
:-(
Raster;
551
552 decode(_,
553 binary,
554 _,
555 #{<<"typreceive">> := <<"enum_recv">>},
556 Enumeration) ->
557
:-(
Enumeration;
558
559 decode(_,
560 binary,
561 _,
562 #{<<"typname">> := <<"void">>},
563 <<>> = VOID) ->
564 1 VOID;
565
566 decode(Parameters, Format, _TypeCache, Type, Value) ->
567
:-(
?LOG_WARNING(#{parameters => Parameters,
568 format => Format,
569 type => Type,
570
:-(
value => Value}),
571
:-(
#{parameters => Parameters, format => Format, type => Type, value => Value}.
572
573
574 numeric(Value) when is_binary(Value)->
575 1 ?FUNCTION_NAME(
576 [fun erlang:binary_to_float/1, fun erlang:binary_to_integer/1], Value);
577
578 numeric(Value) when is_number(Value)->
579 1 ?FUNCTION_NAME(
580 [fun erlang:float_to_binary/1, fun erlang:integer_to_binary/1], Value).
581
582
583 numeric([Conversion | Conversions], Value) ->
584 4 try
585 4 Conversion(Value)
586 catch
587 error:badarg ->
588 2 ?FUNCTION_NAME(Conversions, Value)
589 end;
590
591 numeric([], Value) ->
592
:-(
Value.
593
594
595 triple(X, Y, Z) ->
596 4 list_to_tuple([binary_to_integer(I) || I <- [X, Y, Z]]).
597
598
599 %% encode(Parameters, TypeValue) ->
600 %% ?FUNCTION_NAME(Parameters, TypeValue, pgmp_types:cache()).
601
602
603 -spec encode(map(), [{type_format(), any()}], pgmp_types:cache()) -> iodata().
604
605 encode(Parameters, TypeValue, Types) ->
606 4018 ?FUNCTION_NAME(Parameters, TypeValue, Types, []).
607
608 encode(Parameters, [{_, null} | T], Types, A) ->
609
:-(
?FUNCTION_NAME(Parameters, T, Types, [<<-1:32/signed>> | A]);
610
611 encode(Parameters,
612 [{#{format := Format, type_oid := OID}, Value} | T],
613 Types,
614 A) ->
615 4018 case maps:find(OID, Types) of
616 {ok, Type} ->
617 4018 ?FUNCTION_NAME(
618 Parameters,
619 T,
620 Types,
621 [?FUNCTION_NAME(Parameters,
622 Format,
623 Types,
624 Type,
625 Value) | A]);
626
627 error ->
628
:-(
?FUNCTION_NAME(Parameters,
629 T,
630 Types,
631 [Value | A])
632 end;
633
634 encode(_, [], _, A) ->
635 4017 lists:reverse(A).
636
637
638 encode(Parameters,
639 Format,
640 Types,
641 #{<<"typsend">> := <<"array_send">>,
642 <<"typelem">> := OID},
643 L)
644 when is_list(L),
645 OID /= 0 ->
646 103 {ok, Type} = maps:find(OID, Types),
647 103 array_send(Parameters, Format, Types, Type, L);
648
649 encode(Parameters,
650 Format,
651 Types,
652 #{<<"typsend">> := Send,
653 <<"typelem">> := OID},
654 L)
655 when is_list(L),
656 OID /= 0,
657 Send == <<"int2vectorsend">>;
658 Send == <<"oidvectorsend">> ->
659 3 {ok, Type} = maps:find(OID, Types),
660 3 vector_send(Parameters, Format, Types, Type, L);
661
662 encode(_, _, _, #{<<"typname">> := <<"bpchar">>}, Value) ->
663 2 Value;
664
665 encode(_, text, _, #{<<"typname">> := <<"bytea">>}, Value) ->
666 2 ["\\x", string:lowercase(binary:encode_hex(Value))];
667
668 encode(_, binary, _, #{<<"typname">> := <<"bool">>}, true) ->
669 62 <<1>>;
670
671 encode(_, text, _, #{<<"typname">> := <<"bool">>}, true) ->
672 1 "t";
673
674 encode(_, binary, _, #{<<"typname">> := <<"bool">>}, false) ->
675 40 <<0>>;
676
677 encode(_, text, _, #{<<"typname">> := <<"bool">>}, false) ->
678 1 "f";
679
680 encode(#{<<"integer_datetimes">> := <<"on">>},
681 text,
682 _,
683 #{<<"typname">> := <<"timestamp">>},
684 {{Ye, Mo, Da}, {Ho, Mi, Se}}) when is_integer(Ye),
685 is_integer(Mo),
686 is_integer(Da),
687 is_integer(Ho),
688 is_integer(Mi),
689 is_integer(Se) ->
690 1 io_lib:format(
691 "~4..0b-~2..0b-~2..0b ~2..0b:~2..0b:~2..0b",
692 [Ye, Mo, Da, Ho, Mi, Se]);
693
694 encode(#{<<"integer_datetimes">> := <<"on">>},
695 binary,
696 _,
697 #{<<"typname">> := Name},
698 Timestamp) when is_integer(Timestamp),
699 Name == <<"timestamp">>;
700 Name == <<"timestamptz">> ->
701 200 marshal(int64, pgmp_calendar:encode(Timestamp));
702
703 encode(#{<<"integer_datetimes">> := <<"on">>},
704 text,
705 _,
706 #{<<"typname">> := <<"date">>},
707 {Ye, Mo, Da}) ->
708 1 io_lib:format(
709 "~4..0b-~2..0b-~2..0b",
710 [Ye, Mo, Da]);
711
712
713 encode(#{<<"integer_datetimes">> := <<"on">>},
714 text,
715 _,
716 #{<<"typname">> := <<"time">>},
717 {Ho, Mi, Se}) when is_integer(Ho),
718 is_integer(Mi),
719 is_integer(Se) ->
720 1 io_lib:format(
721 "~2..0b:~2..0b:~2..0b",
722 [Ho, Mi, Se]);
723
724 encode(#{<<"integer_datetimes">> := <<"on">>},
725 binary,
726 _,
727 #{<<"typname">> := <<"time">>},
728 {Ho, Mi, Se} = Time) when is_integer(Ho),
729 is_integer(Mi),
730 is_integer(Se) ->
731 100 marshal(
732 int64,
733 erlang:convert_time_unit(
734 calendar:time_to_seconds(Time), second, microsecond));
735
736 encode(#{<<"integer_datetimes">> := <<"on">>},
737 binary,
738 _,
739 #{<<"typname">> := <<"date">>},
740 {_Ye, _Mo, _Da} = Date) ->
741 103 marshal(
742 int32,
743 calendar:date_to_gregorian_days(Date) -
744 calendar:date_to_gregorian_days(pgmp_calendar:epoch_date(pg)));
745
746 encode(_,
747 binary,
748 _,
749 #{<<"typname">> := <<"jsonb">> = Type}, Value) ->
750 2 [1, (pgmp_config:codec(binary_to_atom(Type))):?FUNCTION_NAME(Value)];
751
752 encode(_,
753 _,
754 _,
755 #{<<"typname">> := Type}, Value)
756 when Type == <<"json">>;
757 Type == <<"jsonb">>;
758 Type == <<"xml">> ->
759 7 (pgmp_config:codec(binary_to_atom(Type))):?FUNCTION_NAME(Value);
760
761 encode(_,
762 binary,
763 _,
764 #{<<"typname">> := <<"uuid">>},
765 <<TimeLow:8/bytes,
766 "-",
767 TimeMid:4/bytes,
768 "-",
769 TimeHi:4/bytes,
770 "-",
771 Clock:4/bytes,
772 "-",
773 Node:12/bytes>>) ->
774 101 lists:map(
775 fun binary:decode_hex/1,
776 [TimeLow, TimeMid, TimeHi, Clock, Node]);
777
778 encode(_,
779 text,
780 _,
781 #{<<"typname">> := <<"uuid">>},
782 <<_:8/bytes,
783 "-",
784 _:4/bytes,
785 "-",
786 _:4/bytes,
787 "-",
788 _:4/bytes,
789 "-",
790 _:12/bytes>> = UUID) ->
791 1 UUID;
792
793 encode(_,
794 binary,
795 _,
796 #{<<"typname">> := Name},
797 Value) when Name == <<"oid">>;
798 Name == <<"regproc">>;
799 Name == <<"regprocedure">>;
800 Name == <<"regoper">>;
801 Name == <<"regoperator">>;
802 Name == <<"regclass">>;
803 Name == <<"regtype">>;
804 Name == <<"regconfig">>;
805 Name == <<"regdictionary">> ->
806 903 <<Value:32>>;
807
808 encode(_,
809 text,
810 _,
811 #{<<"typname">> := Name},
812 Value) when Name == <<"oid">>;
813 Name == <<"regproc">>;
814 Name == <<"regprocedure">>;
815 Name == <<"regoper">>;
816 Name == <<"regoperator">>;
817 Name == <<"regclass">>;
818 Name == <<"regtype">>;
819 Name == <<"regconfig">>;
820 Name == <<"regdictionary">> ->
821 1 integer_to_binary(Value);
822
823 encode(_, binary, _, #{<<"typname">> := Name}, <<Value/bits>>)
824 when Name == <<"bit">>;
825 Name == <<"varbit">> ->
826 101 Length = bit_size(Value),
827 101 Padding = byte_size(Value) * 8 - Length,
828 101 <<Length:32, Value/bits, 0:Padding>>;
829
830 encode(_, text, _, #{<<"typname">> := <<"bit">>}, <<Value/bits>>) ->
831 1 [integer_to_list(I) || <<I:1>> <= Value];
832
833 encode(_, text, _, #{<<"typname">> := <<"float", _/bytes>>}, Value) ->
834 1 numeric(Value);
835
836 encode(_, binary, _, #{<<"typname">> := <<"float", R/bytes>>}, Value) ->
837 200 Size = binary_to_integer(R),
838 200 <<(precision(Value, <<"float", R/bytes>>)):Size/signed-float-unit:8>>;
839
840 encode(_, text, _, #{<<"typname">> := Name}, Value)
841 when Name == <<"int2">>;
842 Name == <<"int4">>;
843 Name == <<"int8">> ->
844 4 integer_to_binary(Value);
845
846 encode(_, binary, _, #{<<"typname">> := <<"numeric">>}, Value) ->
847 105 {Weight, Digits} = numeric_encode(abs(Value)),
848 105 [<<(length(Digits)):16,
849 Weight:16,
850 (case Value of
851 Positive when Positive >= 0 ->
852 51 0;
853
854 _ ->
855 54 16384
856 end):16,
857 0:16>>,
858 103 [<<Digit:16>> || Digit <- Digits]];
859
860 encode(_,
861 binary,
862 _,
863 #{<<"typname">> := <<"tsvector">>},
864 Value) ->
865 3 pgmp_tsvector:encode(Value);
866
867 encode(_,
868 binary,
869 _,
870 #{<<"typname">> := <<"tsquery">>},
871 Value) ->
872 1 pgmp_tsquery:encode(Value);
873
874 encode(_,
875 binary,
876 _,
877 #{<<"typname">> := <<"macaddr8">>},
878 <<Value:8/bytes>>) ->
879 100 Value;
880
881 encode(_,
882 binary,
883 _,
884 #{<<"typname">> := <<"macaddr">>},
885 <<Value:6/bytes>>) ->
886 100 Value;
887
888 encode(_,
889 binary,
890 _,
891 #{<<"typname">> := <<"inet">>},
892 {_, _, _, _} = Value) ->
893 51 [<<2:8, 32:8, 0:8, 4:8>>, tuple_to_list(Value)];
894
895 encode(_,
896 binary,
897 _,
898 #{<<"typname">> := <<"inet">>},
899 {_, _, _, _, _, _, _, _} = Value) ->
900 49 [<<3:8,
901 128:8,
902 0:8,
903 16:8>>,
904 392 [<<Component:16>> || Component <- tuple_to_list(Value)]];
905
906 encode(_, text, _, #{<<"typname">> := <<"inet">>}, Value) ->
907
:-(
Value;
908
909 encode(_,
910 binary,
911 _,
912 #{<<"typname">> := <<"point">>},
913 #{x := X, y := Y}) ->
914 100 <<X:8/float-unit:8, Y:8/float-unit:8>>;
915
916 encode(_,
917 binary,
918 _,
919 #{<<"typname">> := <<"path">>},
920 #{path := open, points := Points}) when is_list(Points) ->
921 49 [<<0:8, (length(Points)):32>>,
922 356 [<<X:8/signed-float-unit:8,
923 Y:8/signed-float-unit:8>> || #{x := X,
924 49 y := Y} <- Points]];
925
926 encode(_,
927 binary,
928 _,
929 #{<<"typname">> := <<"path">>},
930 #{path := closed, points := Points}) when is_list(Points) ->
931 51 [<<1:8, (length(Points)):32>>,
932 657 [<<X:8/signed-float-unit:8,
933 Y:8/signed-float-unit:8>> || #{x := X,
934 51 y := Y} <- Points]];
935
936 encode(_,
937 binary,
938 _,
939 #{<<"typname">> := <<"polygon">>},
940 Points) when is_list(Points) ->
941 100 [<<(length(Points)):32>>,
942 956 [<<X:8/signed-float-unit:8,
943 Y:8/signed-float-unit:8>> || #{x := X,
944 100 y := Y} <- Points]];
945
946 encode(_,
947 binary,
948 _,
949 #{<<"typname">> := <<"circle">>},
950 #{x := X, y := Y, radius := Radius}) ->
951 100 <<X:8/float-unit:8, Y:8/float-unit:8, Radius:8/float-unit:8>>;
952
953 encode(_,
954 binary,
955 _,
956 #{<<"typname">> := Type},
957 {#{x := X1, y := Y1}, #{x := X2, y := Y2}})
958 when Type == <<"lseg">>;
959 Type == <<"box">> ->
960 200 <<X1:8/float-unit:8, Y1:8/float-unit:8,
961 X2:8/float-unit:8, Y2:8/float-unit:8>>;
962
963 encode(_,
964 binary,
965 _,
966 #{<<"typname">> := <<"line">>},
967 #{a := A, b := B, c := C})
968 when A /= 0, B /= 0 ->
969 %% Lines are represented by the linear equation Ax + By + C = 0,
970 %% where A and B are not both zero.
971 100 <<A:8/float-unit:8, B:8/float-unit:8, C:8/float-unit:8>>;
972
973 encode(_, binary, _, #{<<"typname">> := <<"money">>}, Value) when is_integer(Value) ->
974 100 marshal({int, 64}, Value);
975
976 encode(_, binary, _, #{<<"typname">> := Name}, Value)
977 when Name == <<"int2">>;
978 Name == <<"int4">>;
979 Name == <<"int8">> ->
980 1274 <<"int", R/bytes>> = Name,
981 1274 marshal({int, binary_to_integer(R) * 8}, Value);
982
983 encode(_, _, _, #{<<"typname">> := Type}, Value)
984 when Type == <<"varchar">>;
985 Type == <<"char">>;
986 Type == <<"name">>;
987 Type == <<"bytea">>;
988 Type == <<"macaddr8">>;
989 Type == <<"macaddr">>;
990 Type == <<"text">> ->
991 556 Value;
992
993 encode(_, _, _, #{<<"typname">> := <<"void">>}, <<>> = Value) ->
994 1 Value;
995
996 encode(_, text, _, _, Value) ->
997
:-(
Value.
998
999
1000 vector_send(
1001 Parameters,
1002 text = Format,
1003 Types,
1004 Type,
1005 L) ->
1006 2 lists:join(
1007 " ",
1008 2 [encode(Parameters, Format, Types, Type, V) || V <- L]);
1009
1010
1011 vector_send(_, binary, _, #{<<"oid">> := OID}, []) ->
1012
:-(
<<1:32, 0:32, OID:32>>;
1013
1014 %% must be 1-D, 0-based with no nulls
1015 vector_send(
1016 Parameters,
1017 binary = Format,
1018 Types,
1019 #{<<"oid">> := OID} = Type,
1020 L) ->
1021 1 [<<1:32,
1022 0:32,
1023 OID:32,
1024 (length(L)):32,
1025 0:32>>,
1026 2 [size_exclusive(encode(Parameters, Format, Types, Type, V)) || V <- L]].
1027
1028
1029 array_send(_, binary, _, #{<<"oid">> := OID}, []) ->
1030 11 <<0:32, 0:32, OID:32>>;
1031
1032 array_send(
1033 Parameters,
1034 binary = Format,
1035 Types,
1036 #{<<"oid">> := OID} = Type,
1037 L) ->
1038 92 [<<1:32,
1039 (null_bitmap(L)):32,
1040 OID:32,
1041 (length(L)):32,
1042 1:32>>,
1043 lists:map(
1044 fun
1045 (null) ->
1046 1 marshal(int32, -1);
1047
1048 (V) ->
1049 960 size_exclusive(encode(Parameters, Format, Types, Type, V))
1050 end,
1051 L)];
1052
1053 array_send(Parameters, text = Format, Types, Type, L) ->
1054
:-(
["{",
1055 lists:join(
1056 ",",
1057 lists:map(
1058 fun
1059 (Value) ->
1060
:-(
encode(Parameters, Format, Types, Type, Value)
1061 end,
1062 L)),
1063 "}"].
1064
1065
1066 null_bitmap(L) ->
1067 92 case lists:any(
1068 fun
1069 (Value) ->
1070 960 Value == null
1071 end,
1072 L) of
1073 false ->
1074 91 0;
1075
1076 true ->
1077 1 1
1078 end.
1079
1080
1081 array_recv(_, text, _, _, <<>>) ->
1082 1 [];
1083
1084 array_recv(Parameters,
1085 text = Format,
1086 Types,
1087 #{<<"typelem">> := OID},
1088 Values) ->
1089 1 {ok, Type} = maps:find(OID, Types),
1090 1 [decode(Parameters,
1091 Format,
1092 Types,
1093 Type,
1094 1 Value) || Value <- binary:split(Values, <<" ">>, [global])];
1095
1096 array_recv(_, binary, _, _, <<0:32, 0:32, _:32>>) ->
1097 11 [];
1098
1099 array_recv(Parameters,
1100 binary = Format,
1101 Types,
1102 _,
1103 <<1:32, %% NDim
1104 0:32,
1105 OID:32,
1106 _Dimensions:32,
1107 _LowerBnds:32,
1108 Data/bytes>>) ->
1109 102 {ok, Type} = maps:find(OID, Types),
1110 102 [decode(Parameters,
1111 Format,
1112 Types,
1113 Type,
1114 102 Value) || <<Length:32, Value:Length/bytes>> <= Data];
1115
1116 array_recv(Parameters,
1117 binary = Format,
1118 Types,
1119 _,
1120 <<1:32, %% NDim
1121 1:32,
1122 OID:32,
1123 _Dimensions:32,
1124 _LowerBnds:32,
1125 Data/bytes>>) ->
1126 1 {ok, Type} = maps:find(OID, Types),
1127 1 lists:reverse(
1128 pgmp_binary:foldl(
1129 fun
1130 (<<-1:32/signed, R/bytes>>, A) ->
1131 1 {R, [null | A]};
1132
1133 (<<Length:32, Value:Length/bytes, R/bytes>>, A) ->
1134 3 {R, [decode(Parameters, Format, Types, Type, Value) | A]}
1135 end,
1136 [],
1137 Data)).
1138
1139
1140 numeric_decode(<<Length:16,
1141 _:16/signed,
1142 0:16,
1143 _:16,
1144 _:Length/binary-unit:16>>) ->
1145 51 fun
1146 ({Digit, Weight}, A) ->
1147 49 ?LOG_DEBUG(#{digit => Digit, weight => Weight, a => A}),
1148 49 (pow(?NBASE, Weight) * Digit) + A
1149 end;
1150
1151 numeric_decode(<<Length:16,
1152 _:16/signed,
1153 16384:16,
1154 _:16,
1155 _:Length/binary-unit:16>>) ->
1156 54 fun
1157 ({Digit, Weight}, A) ->
1158 54 ?LOG_DEBUG(#{digit => Digit, weight => Weight, a => A}),
1159 54 A - (pow(?NBASE, Weight) * Digit)
1160 end.
1161
1162
1163 pow(X, N) when is_integer(X), is_integer(N), N > 1 ->
1164 2 ?FUNCTION_NAME(1, X, N);
1165 pow(_, 0) ->
1166 99 1;
1167 pow(X, 1) ->
1168 2 X;
1169 pow(X, N) ->
1170
:-(
math:pow(X, N).
1171
1172
1173 pow(Y, _, 0) ->
1174 2 Y;
1175 pow(Y, X, N) when N rem 2 == 0 ->
1176 1 ?FUNCTION_NAME(Y, X * X, N div 2);
1177 pow(Y, X, N) ->
1178 3 ?FUNCTION_NAME(X * Y, X * X, (N - 1) div 2).
1179
1180
1181 numeric_encode(Value) when is_integer(Value) ->
1182 105 ?FUNCTION_NAME(Value, [], 0).
1183
1184 %% ?FUNCTION_NAME(Value, [], 0);
1185 %% numeric_encode(Value) when is_float(Value) ->
1186 %% [Integer, Decimal] = [binary_to_integer(Part) || Part <- binary:split(
1187 %% float_to_binary(
1188 %% Value,
1189 %% [{decimals, 4}]),
1190 %% <<".">>)],
1191
1192 %% ?LOG_DEBUG(#{value => Value,
1193 %% integer => Integer,
1194 %% decimal => Decimal}),
1195
1196 %% {Weight, Digits} = ?FUNCTION_NAME(Integer, [], 0),
1197
1198 %% ?LOG_DEBUG(#{value => Value,
1199 %% weight => Weight,
1200 %% digits => Digits,
1201 %% integer => Integer,
1202 %% decimal => Decimal}),
1203
1204 %% ?FUNCTION_NAME(Decimal, Digits, Weight).
1205
1206
1207 numeric_encode(0, Digits, Weight) ->
1208 105 {Weight - 1, Digits};
1209
1210 numeric_encode(Value, [] = Digits, Weight) ->
1211 104 case Value rem ?NBASE of
1212 0 ->
1213 3 ?FUNCTION_NAME(Value div ?NBASE, Digits, Weight + 1);
1214
1215 Remainder ->
1216 101 ?FUNCTION_NAME(Value div ?NBASE, [Remainder | Digits], Weight + 1)
1217 end;
1218
1219 numeric_encode(Value, Digits, Weight) ->
1220 2 ?FUNCTION_NAME(Value div ?NBASE, [Value rem ?NBASE | Digits], Weight + 1).
1221
1222
1223 precision(Value, <<"float8">>) ->
1224 200 ?FUNCTION_NAME(Value, 15);
1225
1226 precision(Value, <<"float4">>) ->
1227 200 ?FUNCTION_NAME(Value, 6);
1228
1229 precision(Value, Digits) when is_integer(Value) ->
1230
:-(
?FUNCTION_NAME(float(Value), Digits);
1231
1232 precision(Value, Digits) when is_float(Value),
1233 is_integer(Digits) ->
1234 400 Decimals = Digits - trunc(math:ceil(math:log10(trunc(abs(Value)) + 1))),
1235 400 binary_to_float(
1236 iolist_to_binary(
1237 io_lib:fwrite("~.*f", [Decimals, Value]))).
Line Hits Source