諸事情ありまして、先日 Treasure Data (Presto) で 文字列のSHA1を計算する必要が発生しました。(滅多に無いことなのですが。)
その時に方法を検証したので紹介します。ついでにMD5のやり方も紹介します。PythonやMySQLでやる場合は、MD5もSHA1もたいして変わらないのですが、Treasure Data はこの二つは事情がかなり違います。その辺も書いていきたいと思います。
まず、他のDBではどうなっているかということで、MySQLの場合を見ていきます。(僕はAWS RDSのMySQL互換のインスタンスで試してます。)
MySQLには md5 と sha1 という関数が実装されており、これらが「文字列」を受け取り、結果を「16進法表記の文字列」で返してくれます。コメントでつけてるのは結果です。
ドキュメントはこちら: 12.13 暗号化関数と圧縮関数
SELECT
MD5('abcde'), -- ab56b4d92b40713acc5af89985d4b786
SHA1('abcde') -- 03de6c570bfe24bfc328ccd7ca46b76eadaf4334
文字列をそのまま受け取ってくれるし、結果も文字列なので簡単ですね。
Pythonで行う方法は昔記事にしました。
参考: pythonでMD5
文字列をバイト型に変換して、専用の関数を呼び出し、結果がバイト型なのでそれを文字列に変換するのでしたね。
さて、いよいよ本題のTreasure Dataです。まず、MD5の方なのですが、実はTD_MD5というUDFが実装されており、MySQLのMD5と同じように動作します。
参考: Supported Presto and TD Functions – Product Documentation
SELECT
TD_MD5('abcde') -- ab56b4d92b40713acc5af89985d4b786
簡単に使えて結果もMySQLと同じですね。
さて、TD_MD5の存在を知っていたので僕は SHA1についても TD_SHA1が存在すると勘違いしていました。しかし、ドキュメントの先ほどのUDFのページを見ると TD_SHA1はありません。無理矢理書いて実行しても、「Function td_sha1 not registered」と言われます。
では、Treasure Data で SHA1は計算できないのかとも思ったのですが、実はPresto自体に、SHA1という関数が定義さてれいます。(もちろんMD5もあります。)
参考: Binary Functions and Operators — Presto 0.263.1 Documentation
しかし、これつかえばMySQLと同じように動かせる!と思って試したところエラーになりました。
SELECT
SHA1('abcde')
-- 以下エラーメッセージ
-- Unexpected parameters (varchar(5)) for function sha1. Expected: sha1(varbinary)
ドキュメントを見ていただけるとわかる通り、PrestoのMD5やSHA1はバイナリを受け取ってバイナリを返します。要するにPythonのハッシュ関数と似たような動きをするのです。
md5
(binary) → varbinary
Computes the md5 hash of binary
.
sha1
(binary) → varbinary
Computes the sha1 hash of binary
.
ということで、文字列は受け取ってくれないで事前にキャストしましょう。
SELECT
SHA1(CAST('abcde' AS VARBINARY)) -- A95sVwv+JL/DKMzXyka3bq2vQzQ=
CASTしてから渡すと無事に動きました。でも結果が違いますね(実は表記方法が違うだけで中身は違わないのですが。) 結果がバイナリで戻り、(たぶん)BASE64表記で帰ってきたようです。
TO_HEXっていうバイナリを16進法表記の文字列に変換する関数もあるので、それも追加します。
SELECT
TO_HEX(SHA1(CAST('abcde' AS VARBINARY))) -- 03DE6C570BFE24BFC328CCD7CA46B76EADAF4334
MySQLの結果に近づいてきました。ただ、A-Fのアルファベットが大文字になっていますね。LOWERで小文字にしましょう。
SELECT
LOWER(TO_HEX(SHA1(CAST('abcde' AS VARBINARY)))) -- 03de6c570bfe24bfc328ccd7ca46b76eadaf4334
これでMySQLのSHA1と同じ結果が得られました。
ちなみに、MD5の方もこれと同じ流儀で計算できます。
SELECT
LOWER(TO_HEX(MD5(CAST('abcde' AS VARBINARY)))) -- ab56b4d92b40713acc5af89985d4b786
どう考えても TD_MD5 一発で済ませる方が簡単ですね。
将来的にTD_SHA1も実装されることを期待したいです。