例のエラー「符号化方式"UTF8"における0x■ 0x■ 0x■バイトシーケンスを持つ文字は○○○符号化方式では等しくありません」が出た時、原因の文字をクエリで確認する方法。クエリ結果をUTF-8で表示できる環境に限りますが、テキスト型とバイナリ型を橋渡しする関数を使って簡単にできます。PostgreSQL 9.5.2 + Cygwinpsqlで動作確認しました。
Contents


サンプルテーブル作成と、例のエラーを再現

↓ 文字コードUTF-8のデータベース上で、日本語のカナと周辺を連続してテーブルにしました。いくつか、SJISなどユニコード以外で存在しない文字があります。
# create table test_kana as
  select chr(i) from generate_series(12353, 12542) as i;
SELECT 190

# select * from test_kana;
+-----+
| chr |
+-----+
| ぁ  |
| あ  |
| ぃ  |
| い  |
| ぅ  |
| う  |
| ぇ  |
| え  |
| ぉ  |
| お  |
| か  |
| が  |
| き  |
...
| ヱ  |
| ヲ  |
| ン  |
| ヴ  |
| ヵ  |
| ヶ  |
| ヷ  |
| ヸ  |
| ヹ  |
| ヺ  |
| ・  |
| ー  |
| ヽ  |
| ヾ  |
+-----+
(190 rows)


このテーブルを「ユニコード固有の字があると知らずにSJISで出力し、エラーになった」という想定で再現すると ↓ こんな感じ。
# copy test_kana to '/test_kana_sjis.tsv' (encoding sjis);
ERROR:  character with byte sequence 0xe3 0x82 0x94 in 
encoding "UTF8" has no equivalent in encoding "SJIS"

日本語メッセージなら「符号化方式"UTF8"における0xe3 0x82 0x94バイトシーケンスを持つ文字は"SJIS"符号化方式では等しくありません」。最初に出現した原因の文字がUTF-83つのバイトシーケンスで示されてます。この字を特定できれば対処が楽で、UTF-8を表示できる環境なら次のように簡単です。


convert_fromを使う

マニュアルの文字列処理関数のページにあるconvert_fromを ↓ のように使用。エラーメッセージに出たバイトシーケンスを連結し、先頭に\xを付けてバイナリ型にキャストして第1引数にします。第2引数は文字コードUTF-8を指定。当該の文字がフォントにあれば、このように特定できます。
# select convert_from(
    concat('\x', 'e3', '82', '94') :: bytea,
    'utf8');
+--------------+
| convert_from |
+--------------+
| ゔ           |
+--------------+
(1 row)


シーケンスから0xを抜いて引用符で囲むのが面倒なら、正規表現で ↓ こんな風に。別の箇所でエラーが起きた時、メッセージからコピペするのが楽になります。
select convert_from(concat('\x', regexp_replace(
    '0xe3 0x82 0x94',  -- put byte sequence
    ' *0x', '', 'g')) :: bytea, 'utf8');


逆パターンのエラーには使えない

同じようなメッセージ「符号化方式"UTF8"で無効なバイトシーケンスです」の時は、パターンが逆というか少し違って、SJISなどの文字列をそのままUTF-8のデータベースに入れようとしてます。ユニコード固有の文字がなくても起き「原因の文字の特定」は関係ないから、上の方法は使えません。

↓ 現象の例。先ほどのサンプルテーブルから先頭50字をSJISに出力し(普通のカナだからエラーにならない)、そのままUTF-8DBにインポートを試みると、最初の行からUTF-8と違うのでエラーになります。
# copy (select * from test_kana limit 50)
  to '/test_kana_sjis.tsv' (encoding sjis);
COPY 50

# copy test_kana from '/test_kana_sjis.tsv';
ERROR:  invalid byte sequence for encoding "UTF8": 0x82
CONTEXT:  COPY test_kana, line 1


convert_toの使いみち

convert_fromの逆でconvert_toもあり、文字コードのエラー絡みで使う例を考えてみました。非UTF-8で出力する文字の中で「ユニコードONLYの字だったっけ?」と気になるのが出た想定。convert_toに出力文字コードとともに入れて ↓ バイナリ型に変換されれば大丈夫。
# select convert_to('ヾ', 'sjis')
  union all
  select convert_to('ヾ', 'eucjp');
+------------+
| convert_to |
+------------+
| \x8153     |
| \xa1b4     |
+------------+
(2 rows)
-- 両方の文字コードに字が存在

# select convert_to('ゔ', 'sjis')
  union all
  select convert_to('ゔ', 'eucjp');
ERROR:  character with byte sequence 0xe3 0x82 0x94 in 
encoding "UTF8" has no equivalent in encoding "SJIS"
-- 一行目(SJISへの変換)が失敗 = 文字が存在しない


1文字ずつと言わず、文字列まるごと渡せる ↓ のも便利。一つでも当該文字コードにない(ユニコード固有の)字があればエラーになります。
select convert_to('ヴヵヶ', 'sjis')
union all
select convert_to('ヴヵヶ', 'eucjp');
+----------------+
|   convert_to   |
+----------------+
| \x839483958396 |
| \xa5f4a5f5a5f6 |
+----------------+
(2 rows)