Tsrangeはどのようにバイナリで保存されますか?
たとえば、テーブルを作成する
CREATE TABLE public.test (t tsrange);
INSERT INTO test VALUES ('[2010-01-01 14:30, 2010-01-01 15:30)');
INSERT INTO test VALUES ('[2011-01-01 14:31, 2015-11-01 15:30)');
INSERT INTO test VALUES ('[2017-01-01 14:31, 2018-11-01 15:30)');
COPY test TO '/tmp/pgcopy' WITH (FORMAT binary);
COPY test TO '/tmp/pgcopy.csv' WITH (FORMAT csv);
それは出力します:
cat /tmp/pgcopy.csv
"[""2010-01-01 14:30:00"",""2010-01-01 15:30:00"")"
"[""2011-01-01 14:31:00"",""2015-11-01 15:30:00"")"
"[""2017-01-01 14:31:00"",""2018-11-01 15:30:00"")"
hexdump -C /tmp/pgcopy
00000000 50 47 43 4f 50 59 0a ff 0d 0a 00 00 00 00 00 00 |PGCOPY..........|
00000010 00 00 00 00 01 00 00 00 19 02 00 00 00 08 00 01 |................|
00000020 1f 19 f9 a9 aa 00 00 00 00 08 00 01 1f 1a d0 3d |...............=|
00000030 4e 00 00 01 00 00 00 19 02 00 00 00 08 00 01 3b |N..............;|
00000040 c8 89 51 11 00 00 00 00 08 00 01 c6 7b 1a 3a 0e |..Q.........{.:.|
00000050 00 00 01 00 00 00 19 02 00 00 00 08 00 01 e8 08 |................|
00000060 0d 77 11 00 00 00 00 08 00 02 1c 9a dc 4d 0e 00 |.w...........M..|
00000070 ff ff |..|
00000072
1つのフィールドは次のとおりです。
00 00 00 19 02 00 00 00 08 00 01 e8 08 0d 77 11 00 00 00 00 08 00 02 1c 9a dc 4d 0e 00
そこ:
00000019
-25バイトの長さ
02
-ブラケット
00000008
-サブフィールドの長さ
0001e808 0d771100
および00021c9a dc4d0e00
-ミリ秒で保存されたタイムスタンプ。
それを整数のタイムスタンプに変換する方法は?
マイナーなメモとして、COPY .. (WITH BINARY)
には角かっこはありません。これはフラグです(特に括弧を表します)。
COPY ... (WITH BINARY)
から COPY
のドキュメント
実際のタプルデータの適切なバイナリ形式を決定するには、PostgreSQLソース、特に各列のデータ型の
*send
関数と*recv
関数を調べてください(通常、これらの関数はソース配布のsrc/backend/utils/adt/
ディレクトリにあります)。
さらに、ドキュメントはバイナリフォーマットが(現在)持っていると言います
\0\0\0\0
)をスキップします。これらの4バイトに15があった場合、4バイトだけでなく、追加の15もスキップする必要があります。次にタプルは
次に、フィールドがあります
timestamp
またはtsrange
の場合)したがって、最初の列に到達するために基本的に25バイトをスキップします
tsrange
したがって、それは range_send
で指定された形式になっています。上記のコメントで以下のビットで説明されていることがわかります range_recv
バイナリ表現:最初のバイトはフラグ、次に下限(存在する場合)、次に上限(存在する場合)です。 各境界は、4バイトの長さのヘッダーとその境界のバイナリ表現で表現されます(サブタイプのsend関数の呼び出しによって返されます) 。
timestamp
サブタイプあなたの場合、そのサブタイプはタイムスタンプであり、送信は timestamp_send
です。
タイムスタンプは 8バイトとして保存 であることがわかります。これは単純なpq_sendint64
(64ビット/ 8バイトの整数)で送信されるだけです。 timestamp_recv
のしくみを読んで、タイムスタンプのバイナリ表現をどのように処理するかを確認する必要があります。ヒント: timestamp2tm
のメモリ内表現の構造体に入ります
/* timestamp2tm()
* Convert timestamp data type to POSIX time structure.
* Note that year is _not_ 1900-based, but is an explicit full value.
* Also, month is one-based, _not_ zero-based.
* Returns:
* 0 on success
* -1 on out of range
ここではこれ以上楽しませるつもりはありませんが、多分次に行きましょう。
最初に DEADBEEF
8バイトマーカーを追跡するための分離を試みます。
psql -d test -c 'COPY ( SELECT E'\''DEADBEEF'\'' ) TO STDOUT WITH ( FORMAT BINARY );' |
od --skip-bytes=25 --endian big --read-bytes=8 -c
今、それを交換します。
psql -d test -c 'COPY ( SELECT $$2010-01-01 14:30$$::timestamp without time zone ) TO STDOUT WITH ( FORMAT BINARY );' |
od --skip-bytes=25 --endian big --read-bytes=8 --format=d8 -x
結果:括弧コメントが追加されました。
0000031 315671400000000 (timestamp in int8)
0001 1f19 f9a9 aa00 (hex representation)
0000041
そして、それはあなたの最初のタイムゾーンの番号です。上記のセクションのようにtsrange
については、
したがって、最初の内部タイムスタンプにアクセスするために、合計25バイトのスキップに加えて、合計5バイトをスキップします。
psql -d test -c 'COPY ( SELECT $$[2010-01-01 14:30, 2010-01-01 15:30)$$::tsrange ) TO STDOUT WITH ( FORMAT BINARY );' |
od --skip-bytes=30 --endian big --read-bytes=8 --format=d8 -x
これにより、上記と同じ結果が得られます。
0000036 315671400000000
0001 1f19 f9a9 aa00
0000046
--skip-bytes
を42に変更して、その8バイトのタイムスタンプと、lower
の次の4バイトのヘッダーをスキップすると、別のタイムスタンプが取得されます。