org.postgresql.core.EncodingPredictor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of postgresql-holo Show documentation
Show all versions of postgresql-holo Show documentation
PostgreSQL JDBC Driver Postgresql
The newest version!
/*
* Copyright (c) 2003, PostgreSQL Global Development Group
* See the LICENSE file in the project root for more information.
*/
package org.postgresql.core;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.io.IOException;
/**
* Predicts encoding for error messages based on some heuristics.
*
*
* - For certain languages, it is known how "FATAL" is translated
* - For Japanese, several common words are hardcoded
* - Then try various LATIN encodings
*
*/
public class EncodingPredictor {
/**
* In certain cases the encoding is not known for sure (e.g. before authentication).
* In such cases, backend might send messages in "native to database" encoding,
* thus pgjdbc has to guess the encoding nad
*/
public static class DecodeResult {
public final String result;
public final @Nullable String encoding; // JVM name
DecodeResult(String result, @Nullable String encoding) {
this.result = result;
this.encoding = encoding;
}
}
static class Translation {
public final @Nullable String fatalText;
private final String @Nullable [] texts;
public final String language;
public final String[] encodings;
Translation(@Nullable String fatalText, String @Nullable [] texts,
String language, String... encodings) {
this.fatalText = fatalText;
this.texts = texts;
this.language = language;
this.encodings = encodings;
}
}
private static final Translation[] FATAL_TRANSLATIONS =
new Translation[]{
new Translation("ВАЖНО", null, "ru", "WIN", "ALT", "KOI8"),
new Translation("致命错误", null, "zh_CN", "EUC_CN", "GBK", "BIG5"),
new Translation("KATASTROFALNY", null, "pl", "LATIN2"),
new Translation("FATALE", null, "it", "LATIN1", "LATIN9"),
new Translation("FATAL", new String[]{"は存在しません" /* ~ does not exist */,
"ロール" /* ~ role */, "ユーザ" /* ~ user */}, "ja", "EUC_JP", "SJIS"),
new Translation(null, null, "fr/de/es/pt_BR", "LATIN1", "LATIN3", "LATIN4", "LATIN5",
"LATIN7", "LATIN9"),
};
public static @Nullable DecodeResult decode(byte[] bytes, int offset, int length) {
Encoding defaultEncoding = Encoding.defaultEncoding();
for (Translation tr : FATAL_TRANSLATIONS) {
for (String encoding : tr.encodings) {
Encoding encoder = Encoding.getDatabaseEncoding(encoding);
if (encoder == defaultEncoding) {
continue;
}
// If there is a translation for "FATAL", then try typical encodings for that language
if (tr.fatalText != null) {
byte[] encoded;
try {
byte[] tmp = encoder.encode(tr.fatalText);
encoded = new byte[tmp.length + 2];
encoded[0] = 'S';
encoded[encoded.length - 1] = 0;
System.arraycopy(tmp, 0, encoded, 1, tmp.length);
} catch (IOException e) {
continue;// should not happen
}
if (!arrayContains(bytes, offset, length, encoded, 0, encoded.length)) {
continue;
}
}
// No idea how to tell Japanese from Latin languages, thus just hard-code certain Japanese words
if (tr.texts != null) {
boolean foundOne = false;
for (String text : tr.texts) {
try {
byte[] textBytes = encoder.encode(text);
if (arrayContains(bytes, offset, length, textBytes, 0, textBytes.length)) {
foundOne = true;
break;
}
} catch (IOException e) {
// do not care, will try other encodings
}
}
if (!foundOne) {
// Error message does not have key parts, will try other encodings
continue;
}
}
try {
String decoded = encoder.decode(bytes, offset, length);
if (decoded.indexOf(65533) != -1) {
// bad character in string, try another encoding
continue;
}
return new DecodeResult(decoded, encoder.name());
} catch (IOException e) {
// do not care
}
}
}
return null;
}
private static boolean arrayContains(
byte[] first, int firstOffset, int firstLength,
byte[] second, int secondOffset, int secondLength
) {
if (firstLength < secondLength) {
return false;
}
for (int i = 0; i < firstLength; i++) {
for (; i < firstLength && first[firstOffset + i] != second[secondOffset]; i++) {
// find the first matching byte
}
int j = 1;
for (; j < secondLength && first[firstOffset + i + j] == second[secondOffset + j]; j++) {
// compare arrays
}
if (j == secondLength) {
return true;
}
}
return false;
}
}