Pull to refresh

Ручная сборка PostgreSQL для 1C 8.2, или легкие пути не для нас!

Reading time 9 min
Views 24K
В статье рассказывается про ручную сборку базы данных PostgreSQL для 1C под Gentoo Linux.
Не буду рассказывать о всех тупиковых ветвях нашего лабиринта сборки постгреса для 1С. Опишу только то, чего нигде в интернете нет — то, что мы раскурили методом научного тыка. Несмотря на обилие мануалов по установке постгреса для 1C, ни один из них не решил наших основных граблей, все они оказались поверхностными и ничего не объясняли. Поэтому мы посчитали своим долгом восполнить этот пробел :-)


Начну с того, что мы используем Gentoo, и никакие RPM пакеты у нас не ставятся. Пакет 1C в формате rpm для Gentoo установить не представлялось возможным (долго объяснять почему), поэтому скачать src.rpm и автоматически собрать его при помощи rpmbuild отпадало. Нам оставалась единственная возможность — перевести rpm2tgz из rpm формата в простой архив tar.gz и распаковать его, что мы и сделали. А далее приводится пошаговое руководство.

0. Готовим окружение: mkdir /tmp/t5 && cd /tmp/t5 (было 5 стартов с самого начала :-)

1. Качаем с официального сайта пакет со всеми требуемыми файлами: wget v8.1c.ru/overview/postgresql_patches/8-4-1/postgresql-8.4.1-1.1C.src.rpm

2. Выполняем: rpm2tar postgresql-8.4.1-1.1C.src.rpm && rm postgresql-8.4.1-1.1C.src.rpm

3. Распаковываем архив: tar xf postgresql-8.4.1-1.1C.src.tar. Все файлы распакуются в текущую директорию, поэтому лучше все же это делать в чистой папке. Также сразу распакуем postgresql-8.4.1.tar.bz2 и перейдем в эту папку:
tar xfj postgresql-8.4.1.tar.bz2 && cd cd postgresql-8.4.1

4.Открываем файл ../postgresql-8.4.1C.spec и очень долго его изучаем :-) В нем описано все то, что сделал бы rpmbuild (если бы все было так просто!). Находим секцию, где накладываются патчи, и в такой же очередности начинаем это делать (из папки с постгресом):

patch -p1 < ../rpm-pgsql.patch
patch -p1 < ../postgresql-logging.patch
patch -p1 < ../postgresql-perl-rpath.patch
patch -p1 < ../postgresql-prefer-ncurses.patch
patch -p0 < ../1c_FULL_84-0.19.2.patch (p0, не p1!)
patch -p1 < ../postgresql-1c-8.3.patch — пошли приколы, патч не накладывается! И это в официальной поставке. Мы так и не нашли, что меняет rpm spec, чтобы такой патч наложился. Благо, патч этот маленький и понятный, просто меняет опции конфига по умолчанию, поэтому мы потом после установки вернемся, и поправим конфиг ручками. А пока забили на это и идем дальше.
patch -p0 < ../applock-1c-8.4.0.patch

5. Дальше изучаем секцию build rpm spec, и смотрим, с какими параметрами вызывается configure. Трудно верится, что 1С будет использовать perl, python или tcl (а базу мы поднимаем исключительно для 1С, не хочется держать в запиленной разработчиками 1С базе свои другие базы, пусть живут в нормальном оригинальном постгре), поэтому эти опции просто игнорируем. Итого, что мы использовали:
./configure --disable-rpath --enable-nls --disable-integer-datetimes --enable-thread-safety

6. make && make install. Корень постгреса скопируется в /usr/local/pgsql.

Далее надо установить дополнительные библиотеки из папки contrib. Помните, один патч не наложился? Он добавляет в contrib/Makefile 3 дополнительных либы от 1С: mchar, fulleq и fasttrun. Для сборки mchar потребуется дополнительный компонент ICU версии 3.8. Поэтому сделаем небольшое отступление и поставим его:

mkdir /tmp/icu && cd /tmp/icu && wget download.icu-project.org/files/icu4c/3.8.1/icu4c-3_8_1-src.tgz
tar zxf icu4c-3_8_1-src.tgz && cd icu/source
./configure --prefix=/usr/local/icu && make && make install

ICU поставится по отношению к /usr/local/icu. Но нам потребуется лишь подключаемая во время исполнения библиотека, поэтому после установки постргеса мы эту папку прибьем.

Вернемся к сборке contrib.
cd /tmp/t5/postgresql-8.4.1/

7. Надо отредактировать файл contrib/mchar/Makefile и поправить PG_CPPFLAGS на путь к ICU:
PG_CPPFLAGS=-I/usr/local/icu/include.
Также поправить путь в SHLIB_LINK в самом низу в том же файле:
SHLIB_LINK += -L/usr/local/icu/lib -licuuc -l$(ICUNAME) -Wl,-rpath,'$$ORIGIN'

8. Далее добавляем ручками три 1С-ых компонента в WANTED_DIRS в файле contrib/Makefile (38 строка, см. v8.1c.ru/overview/postgresql_patches/8-4-1/postgresql-1c-8.4.patch):
vacuumlo \
mchar \
fulleq \
fasttrun

9. Соберем contrib:
make -C contrib && make -C contrib install

10. Ручками скопируем требуемые ICU библиотеки в папку с либами постре:
cp -a /usr/local/icu/lib/* /usr/local/pgsql/lib/
rm -rf /usr/local/icu

11. Постргес собран и установлен в /usr/local/pgsql. Конфиги пока править не будем. Создадим системный эккаунт, под которым будет работать база:

useradd -d /usr/local/pgsql/ -s /bin/bash pgmaster
mkdir /usr/local/pgsql/data
chown pgmaster /usr/local/pgsql/data && chmod 700 /usr/local/pgsql/data
cat >> /usr/local/pgsql/.bash_profile <<EOF
export LANG=ru_RU.UTF-8
export PGPORT=5434
export PATH=/usr/local/pgsql/bin:/usr/local/pgsql/sbin:$PATH
export LD_LIBRARY_PATH=/usr/local/pgsql/lib
EOF

У нас на сервере уже работает один постргес, поэтому для 1С мы выбрали другой порт и системный эккаунт, создали баш профайл, выполняемый при su — pgmaster, таким образом устанавливаются все пути и переменные.

12. Инициализируем и запустим базу:

mkdir /usr/local/pgsql/log && chown pgmaster /usr/local/pgsql/log
su — pgmaster
initdb -D /usr/local/pgsql/data
postgres -h 0.0.0.0 -p 5434 -D /usr/local/pgsql/data

Сервер тихо запустился без потери управляющего терминала.
Сменим пароль суперюезру (screen рулит, хе-хе):

su — pgmaster
psql template1
psql (8.4.1)
Type «help» for help.
template1=# alter user pgmaster with password 'pass-pass';
ALTER ROLE
template1=#
CTRL+D

А теперь потушим постргрес в первой консоли по CTRL+C, и пора править конфиги.

13. Файл data/pg_hba.conf, в нем надо trust поменять на md5, а также добавить запись, разрешающую заходить по сети:

local all all md5
host all all 127.0.0.1/32 md5
host all all 0.0.0.0/0 md5

14. Файл data/postgresql.conf, в нем надо установить следующие опции (часть из не наложенного патча, часть для отладки):

log_destination = 'stderr'
logging_collector = off # выключим пока генерацию логов в data/pg_log, будет выводить в консоль)
log_statement = 'all' # хотим видеть, что будет 1С выдавать в базу!
listen_addresses = '*' # на каких интерфейсах слушать
effective_cache_size = 512MB # рекомендация 1С
max_locks_per_transaction = 150 # рекомендация 1С

15. Разделяемая память. Либо sysctl -w kernel.shmmax=256000000, либо отредактировать /etc/sysctl.conf, добавить туда kernel.shmmax = 256000000, и далее sysctl -p.

16. А теперь начинается самое интересное. Запускаем снова постргес (postgres -D data -p 5434). Подключаемся и создаем базу для 1C:

$ psql template1
Password:
psql (8.4.1)
Type «help» for help.
template1=#

17. Далее надо в базе template1 создать язык plpgsql:

template1=# create language plpgsql;
CREATE LANGUAGE
template1=#

18. Далее надо залить в нее SQL-код из 3-х компонентов от 1С. Нигде в интернете про эту операцию не сказано. Мы изучили весь RPM spec, выполняли make check, test и тд. Но так и не нашли, каким образом в RPM пакетах в template1 попадает весь этот код. Поэтому мы делаем эту руками:

template1=# \i /tmp/t5/postgresql-8.4.1/contrib/mchar/mchar.sql
template1=# \i /tmp/t5/postgresql-8.4.1/contrib/fulleq/fulleq.sql
template1=# \i /tmp/t5/postgresql-8.4.1/contrib/fasttrun/fasttrun.sql

Если вдруг ругается на подгрузку mchar.so, которая в свою очередь не нашла libicu*, значит забыли про LD_LIBRARY_PATH или скопировать ICU либы в /usr/local/pgsql/lib, читайте сначала :-P

19. Но и это не все!!! Самая главная пробка, из-за которой не могли двигаться далее. Натравливаем на базу 1C, смотрим в логах:

LOG: statement: select 1 from pg_proc where proname in ('plpgsql_call_handler', 'datediff', 'mchar_in', 'mc_mv_icase_cmp', 'fasttruncate')

И после этого 1С говорит: «Плохая у вас база данных, не подходит».
Вводим этот запрос с консоли:

template1=# select 1 from pg_proc where proname in ('plpgsql_call_handler', 'datediff', 'mchar_in', 'mc_mv_icase_cmp', 'fasttruncate');
?column?
— 1
1
1
1
(4 rows)

Четыре единички, а вроде должно быть пять! Методом исключения обнаруживаем, что отсутсвует datediff. Попытки отыскать ее в папке с postgresql и в патчах 1С к успеху не приводят:

snowflake local # cd /tmp/t5/
snowflake t5 # ls
1c_FULL_84-0.19.2.patch pg_config.h postgresql-8.4.1C.spec postgresql.pam
applock-1c-8.4.0.patch postgresql-1c-8.3.patch postgresql-8.4.1.tar.bz2 postgresql-perl-rpath.patch
ecpg_config.h postgresql-8.4.1 postgresql-bashprofile postgresql-prefer-ncurses.patch
filter-requires-perl-Pg.sh postgresql-8.4.1-1.1C.src.tar postgresql.init README.rpm-dist
Makefile.regress postgresql-8.4.1-A4.pdf postgresql-logging.patch rpm-pgsql.patch
snowflake t5 # fgrep -ri datediff *
snowflake t5 #

Нашли в интернете какую-то функцию datediff. Догадались вообще зачем это нужно, и вообще зачем все эти патчи нужны. Вместо адаптации 1С к постргесу, разработчики решили адаптировать постгрес, чтобы SQL был похож на MSSQL! Вот и понапихивали туда функций и типов! Ну да ладно, datediff мы нашли.

На всякий случай вот ее код:
CREATE OR REPLACE FUNCTION datediff(character varying, timestamp without time zone, timestamp without time zone)
RETURNS integer AS
$BODY$
DECLARE
arg_mode alias for $1;
arg_d2 alias for $2;
arg_d1 alias for $3;
BEGIN
if arg_mode = 'SECOND' then
return date_part('epoch',arg_d1) — date_part('epoch',arg_d2);
elsif arg_mode = 'MINUTE' then
return ceil((date_part('epoch',arg_d1) — date_part('epoch',arg_d2)) / 60);
elsif arg_mode = 'HOUR' then
return ceil((date_part('epoch',arg_d1) — date_part('epoch',arg_d2)) /3600);
elsif arg_mode = 'DAY' then
return cast(arg_d1 as date) — cast(arg_d2 as date);
elsif arg_mode = 'WEEK' then
return ceil( ( cast(arg_d1 as date) — cast(arg_d2 as date) ) / 7.0);
elsif arg_mode = 'MONTH' then
return 12 * (date_part('year',arg_d1) — date_part('year',arg_d2))
+ date_part('month',arg_d1) — date_part('month',arg_d2);
elsif arg_mode = 'QUARTER' then
return 4 * (date_part('year',arg_d1) — date_part('year',arg_d2))
+ date_part('quarter',arg_d1) — date_part('quarter',arg_d2);
elsif arg_mode = 'YEAR' then
return (date_part('year',arg_d1) — date_part('year',arg_d2));
end if;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

Внимание! Форматирование текста на хабре заменяет знак минус на дефис. Скопируйте текст процедуры сперва в текстовый редактор, и там массово замените длинный дефис на короткий минус, иначе постгрес будет ругаться :-)

Посчитаем снова единички :-)

template1=# select 1 from pg_proc where proname in ('plpgsql_call_handler', 'datediff', 'mchar_in', 'mc_mv_icase_cmp', 'fasttruncate');
?column?
— 1
1
1
1
1
(5 rows)

Типа ура! Но не тут-то было :-) По 10 раз пересчитываем функции, их пять. И у нас пять! А 1С при запуске все равно говорит, что база у нас плохая, и дальше не идет. Смотрим еще раз все (пару штук всего!) начальные запросы при конфигурировании 1С. Понимаем, что все, что она может, это посчитать еденицы! Но их же 5!!!
Этого результата мы добились часа через 3 после скачивания архива с сайта 1С. Далее начались пляски с бубном. Многочисленные пересборки, попытки качать разные версии, дистрибутивы от Этерсофта… Но на выходе результат один: пять функций, пять едениц. Хоть об стену головой.
И тут пришла в голову идея выполнить эту процедуру на рабочей конфигурации… Высылаем текст по почте товарищу, он скармливает его в базу, и высылает результат:

demo=# select 1 from pg_proc where proname in ('plpgsql_call_handler', 'datediff', 'mchar_in', 'mc_mv_icase_cmp', 'fasttruncate');
?column?
— 1
1
1
1
1
1
(6 rows)

У нас тихий ступор. Мысли шевелятся с трудом. Кто-то замечает, что видимо, каких-то процедур больше одной :-))
Логично. Методом тыка по почте выясняем, что plpgsql_call_handler в двух экзмеплярах. Вызов select * from pg_proc where proname='plpgsql_call_handler' на demo машине выдал две идентичные записи, за исключением поля pronamespace.
Далее долгие попытки руками воткнуть запись в системную таблицу pg_proc, ругань на скармливаемые значения.
Снова ступор. Причина понятна, что делать — нет.
И тут набираем в яндексе одно слово: plpgsql_call_handler (до этого пытались выгуглить сочетания разные). На первом месте сайт www.samag.ru/art/10.2004/10.2004_07.html, где написано:

Далее, нам нужно убедиться, что процедурный язык PL/pgSQL доступен в созданной базе. Для этого выполните следующую команду:

select * from pg_language;
Если plpgsql в полученном результате отсутствует, то из командной оболочки вашей ОС выполните следующую команду:

$ createlang –U pgsql plpgsql eshop
Опция –U задает пользователя – администратора БД, от имени которого будет выполняться данная операция.

Поддержка языка может быть включена и из программы-клиента (например, psql) выполнением следующих двух команд:

create function plpgsql_call_handler()
returns language_handler
as ‘$libdir/plpgsql’, ‘plpgsql_call_handler’
language ‘c’;
create trusted procedural language ‘plpgsql’
handler plpgsql_call_handler;
Первая из них создает функцию-обработчик на базе библиотечной функции, вторая – собственно язык PL/pgSQL на основе созданного выше обработчика.


И тут осенило — создать такую же процедуру еще раз :-P
Заливаем процедуру в базу:
create function plpgsql_call_handler()
returns language_handler
as '$libdir/plpgsql', 'plpgsql_call_handler'
language 'c';

template1=# create function plpgsql_call_handler()
template1-# returns language_handler
template1-# as '$libdir/plpgsql', 'plpgsql_call_handler'
template1-# language 'c';
CREATE FUNCTION
template1=# select * from pg_proc where proname='plpgsql_call_handler';
proname | pronamespace | proowner | prolang | procost | prorows | provariadic | proisagg | proiswindow | prosecdef | proisstrict | proretset | provolatile | pronargs | pronargdefaults | prorettype | proargtypes | proallargtypes | proargmodes | proargnames | proargdefaults | prosrc | probin | proconfig | proacl
----------------------+--------------+----------+---------+---------+---------+-------------+----------+-------------+-----------+-------------+-----------+-------------+----------+-----------------+------------+-------------+----------------+-------------+-------------+----------------+----------------------+-----------------+-----------+--------
plpgsql_call_handler | 11 | 10 | 13 | 1 | 0 | 0 | f | f | f | f | f | v | 0 | 0 | 2280 | | | | | | plpgsql_call_handler | $libdir/plpgsql | |
plpgsql_call_handler | 2200 | 10 | 13 | 1 | 0 | 0 | f | f | f | f | f | v | 0 | 0 | 2280 | | | | | | plpgsql_call_handler | $libdir/plpgsql | |
(2 rows)

Вот они :-)
Проверим:

template1=# select 1 from pg_proc where proname in ('plpgsql_call_handler', 'datediff', 'mchar_in', 'mc_mv_icase_cmp', 'fasttruncate');
?column?
— 1
1
1
1
1
1
(6 rows)

Финал — создаем базу из template1:

template1=# create database enterprise;
CREATE DATABASE

А теперь запускаем 1С и натравливаем на enterprise… О чудо! База данных создалась, и 1С залила всю структуру.

А теперь вопрос. Зачем программистам 1С в базе две одинаковых процедуры???
:-)))

P.S. Потом когда игрались, удаляли datediff, обнаружили, что она зависит от plpgsql_call_handler:
template1=# drop language plpgsql;
ERROR: cannot drop language plpgsql because other objects depend on it
ПОДРОБНО: function datediff(character varying,timestamp without time zone,timestamp without time zone) depends on language plpgsql
Но все равно так и не поняли, откуда она берется в собранных дистрибутивах Етерсофта или 1С :-))
Tags:
Hubs:
+58
Comments 85
Comments Comments 85

Articles