О работе с русскими буквами в InterBase/Firebird

Кузьменко Дмитрий, 1997, последнее обновление – 26.06.2006.

Если вы хотите (или планируете) хранить в базе данных не только русские буквы, но и символы других языков, то вам сюда (в юникод).
 

Установка

После установки InterBase или Firebird в основном каталоге должен быть подкаталог intl, в котором находятся файлы gdsintl.dll (InterBase) или fbintl.dll (Firebird). Если вы не используете инсталлятор, а устанавливаете файлы "поверх" (обновляете) или так как это описано в документе, убедитесь, что распаковка архива (zip) производилась с подкаталогами, то есть после распаковки присутствует каталог intl и в нем есть перечисленные выше файлы.

В файлах gdsintl.dll или fbintl.dll находится ряд кодировок, в том числе и нужная нам WIN1251. Если подкаталога intl или этих файлов нет, то при попытке работы с кодировкой WIN1251 будет выдана ошибка с текстом "text subtype52 not located" (или похожая).
 

Создание базы данных

Для русского языка есть кодировка WIN1251. Также есть устаревшая кодировка CYRL, которая крайне не рекомендуется к использованию.

При создании базы данных необходимо указать кодировку WIN1251 (если вы хотите использовать таблицу русских символов. Для других языков используются другие кодовые страницы). Это приведет к тому, что кодировка WIN1251 будет использована как умолчательная при создании строковых столбцов или переменных в таблицах, view, процедурах, триггерах и т.п. То есть, когда вы указали WIN1251 при создании БД, эта кодировка заносится в столбец RDB$CHARACTER_SET_NAME системной таблицы RDB$DATABASE. И именно отсюда подставляется как умолчательная кодировка для строковых столбцов и переменных.

Если вы не укажете кодировку при создании БД, то в rdb$character_set_name будет указано NONE. То есть все строковые столбцы и переменные, для которых не указана кодировка, будут иметь кодировку NONE, то есть символы будут рассматриваться как "без кодировки". В этом случае можно указывать кодировку строковых столбцо при создании таблиц, процедур и триггеров, однако это достаточно утомительно.

Если вы ошиблись при создании БД, и не указали кодировку, а вам нужна WIN1251 по умолчанию – как можно быстрее извлеките скрипт БД вместе с данными (например в IBExpert), и создайте БД из этого скрипта, указав DEFAULT CHARACTER SET WIN1251 (см. Language Reference, оператор CREATE DATABASE).
 
Внимание! Не стоит пытаться именовать таблицы, поля или любые другие объекты БД русскими буквами. Это не соотвествует стандарту, точно также как и при именовании типов или переменных в языках программирования Pascal или C. В общем это относится и к InterBase/Firebird, где в диалекте 3 можно указывать имена объектов, заключенные в двойных кавычках.


Подсоединение к БД

После того, как вы создали базу данных в WIN1251, необходимо помнить, что для использования этого набора символов нужно его указывать при подключении к базе данных (то есть, в момент подсоединения, как параметр, а не потом, после соединения). То же самое относится и к скриптам  чарсет данных в скрипте (set names win1251) должен быть указан до оператора CONNECT.

Набор символов при коннекте указывается, как правило, в параметрах соединения  ODBC, OLEDB, IBX, dbExpress, BDE и так далее  у них у всех есть параметр, указывающий кодировку при соединении.

У инструментов разработчика  IBExpert, IBConsole и т. п.  в параметрах коннекта также есть возможность указать кодировку WIN1251. И она также должна быть указана до того, как вы подсоединитесь к БД, созданной в кодировке WIN1251.
 

KOI8R

В InterBase 7.5 и Firebird 2.0 есть возможность использовать кодировку KOI8R. Причем, данные могут переконвертироваться из WIN1251 в KOI8R и обратно совершенно прозрачно. Например,
  1. База данных создана в Win1251. При коннекте указывается набор символов KOI8R. Сервер автоматически перекодирует данные из KOI8R в WIN1251 при передаче с клиента на сервер, и наоборот при передаче с сервера на клиента.
  2. База данных создана в KOI8R. То же самое, что и в пункте 1, только в обратную сторону.
 
Внимание! содержимое текстовых blob не переконвертируется, в отличие от строковых столбцов и переменных.


Работа через BDE

После создания БД можно настроить BDE. Для этого достаточно на страничке System, для драйвера INTRBASE (чтобы потом не делать то-же самое каждый раз при создании нового псевдонима) и для псевдонимов INTRBASE установить параметр LANGUAGE DRIVER = Pdox ANSI Cyrillic. Paradox тут ни при чем, просто так называется перекодировщик для кодовой таблицы 1251. Альтернативное название этого языкового драйвера (для указания в SQL Explorer из Delphi 2.0 или Database Desktop/Alias Manager)  ancyrr. Чтобы совсем не запутаться, для конфигурации псевдонимов в Delphi 2.0 для первого раза используйте BDECFG32.EXE  список выбора не даст вам ошибиться. А в Delphi 3.0 даже в SQL Explorer используется теперь тот же список выбора языкового драйвера. В общем, Pdox ANSI Cyrillic  и никаких проблем.
 

Работа через компоненты прямого доступа или API

Здесь неважно, какой именно набор компонент используется  IBObjects, FreeIBComponents, FIBPlus или IBX. Нужно в параметрах компонента, отвечающего за соединение (XXDatabase), вписать строку
lc_ctype=WIN1251

Если же речь идет об инструментах для работы с IB, работающих без BDE, то они должны поддерживать указание нужной кодировки ДО коннекта к базе данных. Например, IBConsole приобрела такую возможность начиная с 319-ой версии. Вместо IBConsole рекомендуется использовать IBExpert или аналогичный альтернативный инструментарий.
 

Скрипты и ISQL

Собственно, об этом было сказано ранее. Т. е. кодировку символов нужно указать перед коннектом или созданием базы данных. Буквально в начале скрипта написать:
SET NAMES WIN1251;

Эта же команда выдается интерактивно перед коннектом к БД через утилиту командной строки ISQL, если консоль показывает символы этой кодировки. Если консоль работает, например, в utf8, то придется указывать
SET NAMES UTF8;
 

Uppercase русских букв

На этом можно было-бы закончить, если-бы иногда не хотелось производить поиск по uppercase строковых полей. Дело в том, что WIN1251 (кроме Firebird 1.5/2.0) не имеет таблицы перевода строчных букв в прописные для русского языка. Зато ее collation order PXW_CYRL  имеет.
 
Замечание. В Firebird 1.5 и последних версиях Yaffil кодировка WIN1251 имеет таблицу upper. Поэтому collate остается необходимым только для обеспечения другой сортировки строковых столбцов (см. дальше). То же самое относится и к поиску по containing.
Строковые типы полей CHAR и VARCHAR имеют специальные параметры дл указания набора хранимых символов и порядка сортировки. Когда база данных создана в определенном DEFAULT CHARACTER SET, этот CHARACTER SET добавляется автоматически ко всем объявлениям строковых полей при создании таблиц. При этом, напомню, CHARACTER SET WIN1251 не имеет таблицы uppercase. Для того, чтобы она была, нужно к объявлению поля добавить фразу COLLATE. Давайте сделаем тестовую таблицу:
CREATE TABLE TESTCHAR(
ID INTEGER NOT NULL PRIMARY KEY,
NAME1 CHAR(30),
NAME2 CHAR(30) COLLATE PXW_CYRL)

И занесем в эту таблицу (хотя бы при помощи SQL Explorer) следующие данные:
 
1 А А
2 Б Б
3 В В
4 а а
5 б б
6 в в
Теперь, выполните три запроса и посмотрите в каком порядке будут возвращаться записи:
  1. SELECT * FROM TESTCHAR ORDER BY NAME1
  2. SELECT * FROM TESTCHAR ORDER BY NAME2
  3. SELECT * FROM TESTCHAR ORDER BY NAME1 COLLATE PXW_CYRL

Обратите внимание, что второй и третий запросы привели к одинаковому порядку записей в результате. Действительно, в любом месте и в любой ситуации можно добавить спецификатор COLLATE PXW_CYRL, если при создании таблицы у строкового поля этот спецификатор указан не был.

Например, если нужно сделать поиск по uppercase-значению, даже когда поле было создано без collate
SELECT * FROM TESTCHAR WHERE UPPER(NAME1 COLLATE PXW_CYRL) = 'А'

Естественно, что для поля NAME2 такая конструкция не понадобится, и совершенно нормально отработает запрос
SELECT * FROM TESTCHAR WHERE UPPER(NAME2) = 'А'
 
Замечание. В Yaffil и Firebird такой запрос отработает на полях NAME1 и NAME2 одинаково. Потому что как было указано выше, в этих серверах кодировка WIN1251 по умолчанию содержит таблицу upper, и collate в нанном случае не нужен.
 
Примечание. Если у вас есть индекс по полю NAME2, то использование любой функции (UPPER, или подключенных UDF) приведет к тому, что оптимизатор не сможет использовать индекс для ускорения запроса. В этом случае запрос SELECT * FROM TESTCHAR WHERE NAME2 = 'А' будет оптимизирован (поиск по индексу), а запрос SELECT * FROM TESTCHAR WHERE UPPER(NAME2) = 'А' приведет к перебору всех записей таблицы. Если у вас много записей в подобной таблице, и всего одно условие WHERE, содержащее UPPER, то вы можете добавить в таблицу дополнительное поле с DEFAULT UPPER(SOURCEFIELD). В этом случае новые значени добавляются только в SOURCEFIELD, а поиск ведется по полю, содержащему готовый UPPER.
 
Примечание. Указание COLLATE PXW_CYRL приводит к тому, что IB считает такие поля хранящими символы в 3-байтовой кодировке, и не позволяет создать индекс по такому полю если его длина превышает 84 (252/3=84) символа. В таком случае лучше отказаться от стандартного UPPER, и использовать собственные UDF для перевода регистра букв. Пример функций UpCase и LoCase с исходным текстом на Delphi и готовой DLL можно взять здесь – caseudf.zip (10K). В общем, указывать COLLATE PXW_CYRL или нет, вы должны решить для себя самостоятельно. В основном выбор определяется необходимостью специфической сортировки прописных и строчных русских букв, которую обеспечивает COLLATE PXW_CYRL (см. выше запросы с order by).
 

Как сменить COLLATE на работающей базе данных

Действительно, что делать если база данных уже работает, поля были созданы без указания COLLATE, а вдруг для какого-то поля понадобилось использовать функцию UPPER? Выход есть, и он более прост чем, например, изменение длины строкового поля.
 
Примечание. Вы должны помнить, что в простейшем случае такая задача решается написанием
SELECT * FROM MYTABLE
WHERE UPPER(MYFIELD COLLATE PXW_CYRL) = 'ВАСЯ'
То же самое относится и к ORDER BY COLLATE PXW_CYRL. Таким образом, все изложенное ниже стоит использовать только в крайнем случае, когда никакие другие стандартные методы не работают. Помните, что прямое редактирование системных таблиц – опасная операция, которая, как минимум, не должна выполняться на рабочем сервере.

Для этого вам понадобится отредактировать системную таблицу RDB$RELATION_FIELDS. Первые два столбца этой таблицы – RDB$FIELD_NAME и RDB$RELATTION_NAME, т.е. имя поля и имя таблицы соответственно. Найдите запись, у которой эти поля имеют нужное вам значение – например, MYFIELD и MYTABLE.

Последний столбец таблицы RDB$RELATION_FIELDS называется RDB$COLLATION_ID. В нем записан 0, если поле имеет только CHARACTER SET WIN1251, и 1 – если поле было создано с CHARACTER SET WIN1251 COLLATE PXW_CYRL.

Поэтому просто поменяйте 0 на 1, но только там, где RDB$COLLATION_ID не имеет значения "пусто".

Теперь, чтобы существующие в таблице записи получили новый COLLATE, нужно сделать
UPDATE MYTABLE
SET MYFIELD=MYFIELD

 
Внимание! Если по данному столбцу есть индекс, его нужно отключить (alter index nnn inactive) до обновления столбца, и включить (alter index nnn active) после обновления.
Иначе UPPER будет работать только для вновь создаваемых записей, но не для старых – это вызвано тем, что InterBase хранит информацию о типах полей в blob таблицы RDB$FORMATS, что позволяет менять тип поля, длину, и например COLLATE прямо "на ходу".

Ну и чтобы проверить, правильно-ли работает UPPER, можно выдать запрос
SELECT UPPER(MYFIELD) FROM MYTABLE
 
Напомню, что в Firebird 1.5 и 2.0 для Win1251 UPPER работает без указания collate.
Если вы хотите изменить "одним махом" COLLATE у всех полей вашей БД, то можете выполнить запрос
UPDATE RDB$RELATION_FIELDS
SET RDB$COLLATION_ID = 1
WHERE
(RDB$FIELD_NAME NOT CONTAINING '$') AND
(RDB$SYSTEM_FLAG = 0) AND
(RDB$COLLATION_ID = 0)

Затем, естественно, update придется выполнить для каждой таблицы, в которой вы хотите для строковых полей работать с UPPER. Если по измененным строковым полям есть индексы, то нужно эти индексы удалить и пересоздать, т. к. order by будет использовать для сортировки значения ключа, а не значения поля в таблице.
 
Примечание. Изменение системных таблиц является нормальной операцией, если вы точно знаете что хотите сделать, и ознакомились с разделом System Tables в книге InterBase Language Reference. InterBase может выполнять изменение структуры таблиц, типов полей и т.п. операции, однако для них нет соответствующих команд SQL – они просто не определены в стандарте.


ODBC-драйвер

Для нормальной работы нужен ODBC-драйвер Firebird, EasySoft или DataDirect (IB2007). Они (сейчас уже практически и любые другие, в т. ч. бесплатные) позволяют установить на уровне настроек нужную кодировку, например WIN1251.

Драйвер InterSolv, позволявший установить WIN1251, поставлявшийся в IB 5.5 и 5.6 более не поддерживается и не выпускается.

Если вы работаете с драйвером, который не поддерживает установку кодировки при коннекте, то тогда у всех SQL-запросов придется указывать кодировку символов вручную. Например,
INSERT INTO TESTCHAR VALUES (:ID, _win1251 :NAME1, _win1251 :NAME2)
 

InterClient

С разрешения Сергея Астахова, владельца страницы http://people.comita.spb.ru/users/sergeya/java/ruschars.html, приводится часть раздела, описывающая работу с русскими буквами в InterClient:
InterBase (interbase.interclient.Driver)

Для этого драйвера работает параметр "charSet":
// Параметры соединения с базой
Properties connInfo = new Properties();

connInfo.put("user", username);
connInfo.put("password", password);
connInfo.put("charSet", "Cp1251");

// Устанавливаем соединение
Connection db = DriverManager.getConnection(dataurl, connInfo);

Однако не забудьте при создании БД и таблиц указать кодировку символов. Для русского языка можно использовать значения "UNICODE_FSS" или "WIN1251".

В версии 2.01 InterClient присутствует ошибка – классы ресурсов с сообщениями для русского языка там неправильно скомпилированы. Скорей всего разработчики просто забыли указать кодировку исходников при компиляции. Есть два пути исправления этой ошибки:
  • Использовать interclient-core.jar вместо interclient.jar. При этом русских ресурсов просто не будет, и автоматом подхватятся английские.
  • Перекомпилировать файлы в нормальный Unicode. Разбор class-файлов – дело неблагодарное, поэтому лучше воспользоваться JAD-ом. К сожалению JAD, если встречает символы из набора ISO-8859-1, выводит их в 8-ричной кодировке, так что воспользоваться стандартным перекодировщиком native2ascii не удастся – придётся написать свой (программа Decode). Если Вам не хочется заморачиваться с этими проблемами – можете просто взять готовый файл с ресурсами (пропатченый jar с драйвером – interclient.jar, отдельные классы ресурсов – interclient-rus.jar).

Но даже настроив JDBC-драйвер на нужную кодировку в некоторых случаях можно нарваться на неприятности. Например, при попытке использования новых замечательных скролируемых курсоров стандарта JDBC 2 в мосте JDBC-ODBC из JDK 1.3.x довольно быстро можно обнаружить, что русские буквы там просто не работают (метод updateString()).
 
Замечание. Все версии InterClient можно найти в разделе «Компоненты и драйверы».
 

Русские буквы в хранимых процедурах, триггерах и exceptions

 
Внимание! Не используйте IBConsole, пользуйтесь нормальными инструментами – IBExpert, IBManager и т. п. (см. Download).
Не возбраняется, однако будут некоторые проблемы. Дело в том, что все метаданные хранятся в универсальной многоязычной кодировке UNICODE_FSS. Поэтому при создании триггеров и хранимых процедур, содержащих в тексте русские буквы, рекомендуется
  • делать это из WISQL, перед подсоединением к БД установив Character set on WIN1251;
  • перед извлечением метаданных в WISQL (Extract Metadata for database), опять же ДО подсоединения к БД нужно установить Character set on UNICODE_FSS;
  • если это возможно, избавиться от строковых констант с русскими буквами в теле триггера или процедуры. Для процедуры, например, можно передавать строку с русскими буквами как параметр – это в дальнейшем может избавить вас и от переписывания этой процедуры;
  • в SQL Explorer при попытке просмотра таких процедур, триггеров и exceptions будет возникать exception о "неверном преобразовании символов". Это решается созданием отдельного алиаса с пустым LANGDRIVER специально для работы с метаданными. Т.е. в алиасе с Pdox ansi Cyrillic вы работаете с данными, а в алиасе без языкового драйвера – с "русскими" exceptions, комментариями и текстом в триггерах и процедурах.
 

Не надо

Из-за ошибки в GBAK (вплоть до IB 6.0 включительно) не рекомендуется использовать в качестве списка допустимых значений для строковых полей русские буквы при описании структуры таблицы (default value list, например, flag char(3) default 'да'). Лучше перенести эту часть в триггер, если такая проверка действительно необходима на сервере. Если-же все-таки это случилось, восстановить базу данных удастся только при установленном параметре GBAK Do not restore validity conditions (в противном случае БД не восстановится, см. Verbose Output).

В Firebird версии 0.9.5.156 и выше этот баг устранен, однако чтобы он не возникал, требуется создание базы данных именно в этой версии. Backup/Restore для исправления бага недостаточно.

Еще один вариант решения проблемы с русскими default – это создание их в следующей форме:
create table bug_bugbase (
s varchar(20) character set win1251 collate win1251
default _win1251 'XXXX'
not null);

Это даст указание однозначно идентифицировать character set для символов, указываемых в default.
 

Подключение своих наборов символов

Такая возможность есть, и она включая примеры описана в CollateKit и ibCollate на http://www.brookstonesystems.com/. Однако перед публикацей исходников IB в OpenSource при вычистке кода, управляющего лицензиями, была допущена ошибка, в результате чего CollateKit до сих пор не работал ни с бесплатным InterBase, ни с Firebird. В билде Firebird старше ~1.0.0.500 эта ошибка устранена, и кодировки можно подключать. Однако при этом под Windows надо внимательно следить за версией файла GDSINTL.DLL (под Unix – gdsintl.so) – она должна точно совпадать с версией Firebird, в противном случае кодировки работать не будут (вообще, даже не имея в виду создаваемые самостоятельно кодировки).

Вот и все. Если вам известно еще что-нибудь, относящееся к этой теме, напишите.

Подпишитесь на новости Firebird в России

Подписаться