私は現在、ハッシュを含み、bytea形式で格納されているテーブルに取り組んでいます。ただし、ハッシュを16進文字列に変換すると、バイトの順序が正しくなくなります。例:
SELECT encode(hash, 'hex') FROM mytable LIMIT 1;
Output: 1a6ee4de86143e81
Expected: 813e1486dee46e1a
すべてのエントリのバイトの順序を逆にする方法はありますか?
これを行う1つの方法を次に示しますが、これを行うことは決してありません。データベースのbytea
列にバイトを格納することに問題はありません。しかし、私はデータベースに少し絡みませんでした。
これはsql-esqueであり、機能するはずです-これが私たちがやっていることです、
ここに例があります
CREATE TABLE foo AS SELECT '\x813e1486dee46e1a'::bytea AS bar;
SELECT bar, string_agg(to_hex(byte), '') AS hash
FROM foo
CROSS JOIN LATERAL (
SELECT get_byte(bar,"offset") AS byte
FROM generate_series(0,octet_length(bar)-1) AS x("offset")
ORDER BY "offset" DESC
) AS x
GROUP BY bar;
2つのメモ
offset
を使用できませんが、要点はわかります。エンコードされた表現をテキストとして扱い、正規表現を使用してバイト単位で逆にすることができます。
SELECT string_agg(reverse(b[1]),'')
FROM regexp_matches(reverse(encode('STUFF','hex')),'..','g')b;
別の(より冗長な)メソッド:
WITH bytes AS (
SELECT row_number() over() AS n, byte[1]
FROM regexp_matches( encode( 'STUFF', 'hex' ), '..', 'g' ) AS byte
), revbytes AS (
SELECT * FROM bytes ORDER BY n DESC
)
SELECT array_to_string(array_agg(byte),'')
FROM revbytes;
使用例:
(filip@[local:/var/run/postgresql]:5432) filip=# SELECT encode( 'STUFF', 'hex' );
encode
------------
5354554646
(1 row)
(filip@[local:/var/run/postgresql]:5432) filip=# SELECT string_agg(reverse(b[1]),'')FROM regexp_matches(reverse(encode('STUFF','hex')),'..','g')b;
string_agg
------------
4646555453
(1 row)
bytea
値のバイトを逆にするだけでよい場合は、 plpythonu
を使用する(比較的)シンプルで高速なソリューションがあります。
create or replace function reverse_bytea(p_inp bytea) returns bytea stable language plpythonu as $$
b = bytearray()
b.extend(p_inp)
b.reverse()
return b
$$;
select encode(reverse_bytea('\x1a6ee4de86143e81'), 'hex');
----
813e1486dee46e1a
しかし、私はデータ自体に何か問題があると思います(ストレージの方法、データの解釈...)
Vanilla Postgresのツールを使用したソリューション:
両方のソリューションに列_bytea_reverse
_を追加しました。必要がなければ削除してください。
get_byte()
の場合:
_SELECT t.b, text_reverse, decode(text_reverse, 'hex') AS bytea_reverse
FROM tbl t
LEFT JOIN LATERAL (
SELECT string_agg(to_hex(get_byte(b, x)), '') AS text_reverse
FROM generate_series(octet_length(t.b) - 1, 0, -1) x
) x ON true;
_
これは @ Evanが提供したもの に似ています。彼の優れた説明のほとんどが当てはまります。だが:
LEFT JOIN LATERAL ... ON true
_を使用すると、NULL値を持つ行が失われます。generate_series()
は逆数を提供できるため、別の_ORDER BY
_ステップは必要ありません。LATERAL
結合を使用しながら、サブクエリで集計します。エラーが発生しにくく、より複雑なクエリと統合しやすく、外部クエリで_GROUP BY
_を使用する必要がありません。regexp_matches()
の場合:
_SELECT t.b, text_reverse, decode(text_reverse, 'hex') AS bytea_reverse
FROM tbl t
LEFT JOIN LATERAL (
SELECT string_agg(byte[1], '' ORDER BY ord DESC) AS text_reverse
FROM regexp_matches(encode(t.b, 'hex' ), '..', 'g' ) WITH ORDINALITY AS x(byte, ord)
) x ON true;
_
これは "verbose"バリアント@filipremが提供される に似ています。だが:
LEFT JOIN LATERAL ... ON true
_を使用すると、NULL値を持つ行が失われます。WITH ORDINALITY
_を使用して、「無料」で行番号を取得します。したがって、row_number()
を使用した別のサブクエリも、二重のreverse()
も必要ありません。詳細:SOで同様の質問:
すべての提案のおかげで、私は必要に応じて機能するこのC言語関数を書きました:
#include "postgres.h"
#include "fmgr.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
Datum bytea_custom_reverse(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(bytea_custom_reverse);
Datum
bytea_custom_reverse(PG_FUNCTION_ARGS) {
bytea *data = PG_GETARG_BYTEA_P_COPY(0);
unsigned char *ptr = (unsigned char *) VARDATA(data);
int32 dataLen = VARSIZE(data) - VARHDRSZ;
unsigned char *start, *end;
for ( start = ptr, end = ptr + dataLen - 1; start < end; ++start, --end ) {
unsigned char swap = *start;
*start = *end;
*end = swap;
}
PG_RETURN_BYTEA_P(data);
}
このスレッドを助けてくれてありがとう。そして、これは、回答に応じて、BitConverter.GetBytes()を使用して、C#のようなlittleEndianでbigintをbyteaに変換するという私の選択です。
with mycte as (
select int8send(394112768534335::bigint) as conversionValue
)
SELECT decode(string_agg (
(case when get_byte(conversionValue, x)<= 15 then ('0') else '' end) ||
to_hex(get_byte(conversionValue, x))
, ''), 'hex') AS nativeId_reverse
FROM mycte, generate_series(octet_length(conversionValue) - 1, 0, -1) as x;
BigintプレゼンテーションによってlittleEndian byteAとしてpostgresqlに配置された検索値の場合:
with mycte as (
select int8send(394112768534335::bigint) as conversionValue
)
Select * FROM mycte, *SomeByteaFieldTable*
where *SomeByteaId* =
(SELECT decode(string_agg (
(case when get_byte(conversionValue, x)<= 15 then ('0') else '' end) ||
to_hex(get_byte(conversionValue, x))
, ''), 'hex') AS nativeId_reverse
FROM generate_series(octet_length(conversionValue) - 1, 1, -1) x);