diff --git a/trytond.spec b/trytond.spec index bb6eb3a..31e0afb 100644 --- a/trytond.spec +++ b/trytond.spec @@ -35,11 +35,12 @@ Source3: %{name}.conf Source4: %{name}_log.conf # GNU Health patch for user login Patch0: trytond_get_login.patch +Patch1: trytond42_psql10.diff Source20: %{name}.service # List of additional build dependencies BuildRequires: fdupes BuildRequires: postgresql -BuildRequires: postgresql96-server +BuildRequires: postgresql-server BuildRequires: python3-Werkzeug BuildRequires: python3-bcrypt BuildRequires: python3-lxml @@ -50,7 +51,7 @@ BuildRequires: python3-setuptools BuildRequires: python3-wrapt BuildRequires: systemd-rpm-macros Requires: libreoffice-pyuno -Requires: postgresql96-server +Requires: postgresql-server Requires: python3-Genshi Requires: python3-Levenshtein Requires: python3-Sphinx @@ -69,7 +70,7 @@ Requires: unoconv Requires(pre): /usr/sbin/groupadd Requires(pre): /usr/sbin/useradd # Post requires update-alternatives to install tool update-alternatives. -Requires(post): update-alternatives +#Requires(post): update-alternatives BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildArch: noarch @@ -88,6 +89,7 @@ security. cp %{S:1} . cp %{S:2} . %patch0 -p0 +%patch1 -p1 -F2 %build python3 setup.py build @@ -115,7 +117,7 @@ getent passwd tryton > /dev/null || %{_sbindir}/useradd -r -g tryton \ %post %service_add_post trytond.service # Postgresql 96 is required for tryton series 4.2.x! -update-alternatives --set postgresql /usr/lib/postgresql96 +#update-alternatives --set postgresql /usr/lib/postgresql96 %preun %service_del_preun trytond.service diff --git a/trytond42_psql10.diff b/trytond42_psql10.diff new file mode 100644 index 0000000..493a4c5 --- /dev/null +++ b/trytond42_psql10.diff @@ -0,0 +1,504 @@ +# Copyright (c) 2018 Mathias Behrle +diff --git a/trytond/backend/database.py b/trytond/backend/database.py +index 22567cd61c7683ee88cb0e38c732a3a5ffb6b887..bff64d62fb45d0dbb45de5972375cb76414f1be4 100644 +--- a/trytond/backend/database.py ++++ b/trytond/backend/database.py +@@ -172,3 +172,46 @@ class DatabaseInterface(object): + def has_multirow_insert(self): + 'Return True if database supports multirow insert' + return False ++ ++ @classmethod ++ def has_sequence(cls): ++ "Return if database supports sequence querying and assignation" ++ return False ++ ++ def sequence_exist(self, connection, name): ++ "Return if a sequence exists" ++ if not self.has_sequence(): ++ return ++ raise NotImplementedError ++ ++ def sequence_create( ++ self, connection, name, number_increment=1, start_value=1): ++ "Creates a sequence" ++ if not self.has_sequence(): ++ return ++ raise NotImplementedError ++ ++ def sequence_update( ++ self, connection, name, number_increment=1, start_value=1): ++ "Modifies a sequence" ++ if not self.has_sequence(): ++ return ++ raise NotImplementedError ++ ++ def sequence_rename(self, connection, old_name, new_name): ++ "Renames a sequence" ++ if not self.has_sequence(): ++ return ++ raise NotImplementedError ++ ++ def sequence_delete(self, connection, name): ++ "Removes a sequence" ++ if not self.has_sequence(): ++ return ++ raise NotImplementedError ++ ++ def sequence_next_number(self, connection, name): ++ "Gets the next number of a sequence" ++ if not self.has_sequence(): ++ return ++ raise NotImplementedError +diff --git a/trytond/backend/mysql/table.py b/trytond/backend/mysql/table.py +index bfe6031250787a0ed0d593dd4822ec25bb596b55..c09065f37ccf2c242007e69a44bf111e4e1e8632 100644 +--- a/trytond/backend/mysql/table.py ++++ b/trytond/backend/mysql/table.py +@@ -75,14 +75,6 @@ class TableHandler(TableHandlerInterface): + cursor.execute('ALTER TABLE `%s` RENAME TO `%s`' + % (old_history, new_history)) + +- @staticmethod +- def sequence_exist(sequence_name): +- return True +- +- @staticmethod +- def sequence_rename(old_name, new_name): +- pass +- + def column_exist(self, column_name): + return column_name in self._columns + +diff --git a/trytond/backend/postgresql/database.py b/trytond/backend/postgresql/database.py +index 505973a8785ca5c11889aa757222a9242acb182a..6967b7d28c73d1968b216d57814a604f5af10199 100644 +--- a/trytond/backend/postgresql/database.py ++++ b/trytond/backend/postgresql/database.py +@@ -2,7 +2,6 @@ + # this repository contains the full copyright notices and license terms. + import time + import logging +-import re + import os + import urllib + from decimal import Decimal +@@ -34,8 +33,6 @@ __all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError'] + + logger = logging.getLogger(__name__) + +-RE_VERSION = re.compile(r'\S+ (\d+)\.(\d+)') +- + os.environ['PGTZ'] = os.environ.get('TZ', '') + + +@@ -131,10 +128,11 @@ class Database(DatabaseInterface): + def get_version(self, connection): + if self.name not in self._version_cache: + cursor = connection.cursor() +- cursor.execute('SELECT version()') ++ cursor.execute('SHOW server_version_num') + version, = cursor.fetchone() +- self._version_cache[self.name] = tuple(map(int, +- RE_VERSION.search(version).groups())) ++ major, rest = divmod(int(version), 10000) ++ minor, patch = divmod(rest, 100) ++ self._version_cache[self.name] = (major, minor, patch) + return self._version_cache[self.name] + + @staticmethod +@@ -353,6 +351,80 @@ class Database(DatabaseInterface): + self.put_connection(connection) + return self._search_path + ++ @classmethod ++ def has_sequence(cls): ++ return True ++ ++ def sequence_exist(self, connection, name): ++ cursor = connection.cursor() ++ for schema in self.search_path: ++ cursor.execute('SELECT 1 ' ++ 'FROM information_schema.sequences ' ++ 'WHERE sequence_name = %s AND sequence_schema = %s', ++ (name, schema)) ++ if cursor.rowcount: ++ return True ++ return False ++ ++ def sequence_create( ++ self, connection, name, number_increment=1, start_value=1): ++ cursor = connection.cursor() ++ ++ param = self.flavor.param ++ cursor.execute( ++ 'CREATE SEQUENCE "%s" ' ++ 'INCREMENT BY %s ' ++ 'START WITH %s' ++ % (name, param, param), ++ (number_increment, start_value)) ++ ++ def sequence_update( ++ self, connection, name, number_increment=1, start_value=1): ++ cursor = connection.cursor() ++ param = self.flavor.param ++ cursor.execute( ++ 'ALTER SEQUENCE "%s" ' ++ 'INCREMENT BY %s ' ++ 'RESTART WITH %s' ++ % (name, param, param), ++ (number_increment, start_value)) ++ ++ def sequence_rename(self, connection, old_name, new_name): ++ cursor = connection.cursor() ++ if (self.sequence_exist(connection, old_name) ++ and not self.sequence_exist(connection, new_name)): ++ cursor.execute('ALTER TABLE "%s" RENAME TO "%s"' ++ % (old_name, new_name)) ++ ++ def sequence_delete(self, connection, name): ++ cursor = connection.cursor() ++ cursor.execute('DROP SEQUENCE "%s"' % name) ++ ++ def sequence_next_number(self, connection, name): ++ cursor = connection.cursor() ++ version = self.get_version(connection) ++ if version >= (10, 0): ++ cursor.execute( ++ 'SELECT increment_by ' ++ 'FROM pg_sequences ' ++ 'WHERE sequencename=%s ' ++ % self.flavor.param, ++ (name,)) ++ increment, = cursor.fetchone() ++ cursor.execute( ++ 'SELECT CASE WHEN NOT is_called THEN last_value ' ++ 'ELSE last_value + %s ' ++ 'END ' ++ 'FROM "%s"' % (self.flavor.param, name), ++ (increment,)) ++ else: ++ cursor.execute( ++ 'SELECT CASE WHEN NOT is_called THEN last_value ' ++ 'ELSE last_value + increment_by ' ++ 'END ' ++ 'FROM "%s"' % name) ++ return cursor.fetchone()[0] ++ + register_type(UNICODE) + if PYDATE: + register_type(PYDATE) +diff --git a/trytond/backend/postgresql/table.py b/trytond/backend/postgresql/table.py +index a3323041c98be880cf9053b98589dc78e16265b5..8cae6ca12fcacf5a5cdd924416f26ef0ca7ca7bf 100644 +--- a/trytond/backend/postgresql/table.py ++++ b/trytond/backend/postgresql/table.py +@@ -22,8 +22,10 @@ class TableHandler(TableHandlerInterface): + transaction = Transaction() + cursor = transaction.connection.cursor() + # Create sequence if necessary +- if not self.sequence_exist(self.sequence_name): +- cursor.execute('CREATE SEQUENCE "%s"' % self.sequence_name) ++ if not transaction.database.sequence_exist( ++ transaction.connection, self.sequence_name): ++ transaction.database.sequence_create( ++ transaction.connection, self.sequence_name) + + # Create new table if necessary + if not self.table_exist(self.table_name): +@@ -81,7 +83,8 @@ class TableHandler(TableHandlerInterface): + + @staticmethod + def table_rename(old_name, new_name): +- cursor = Transaction().connection.cursor() ++ transaction = Transaction() ++ cursor = transaction.connection.cursor() + # Rename table + if (TableHandler.table_exist(old_name) + and not TableHandler.table_exist(new_name)): +@@ -90,7 +93,8 @@ class TableHandler(TableHandlerInterface): + # Rename sequence + old_sequence = old_name + '_id_seq' + new_sequence = new_name + '_id_seq' +- TableHandler.sequence_rename(old_sequence, new_sequence) ++ transaction.database.sequence_rename( ++ transaction.connection, old_sequence, new_sequence) + # Rename history table + old_history = old_name + "__history" + new_history = new_name + "__history" +@@ -99,30 +103,6 @@ class TableHandler(TableHandlerInterface): + cursor.execute('ALTER TABLE "%s" RENAME TO "%s"' + % (old_history, new_history)) + +- @classmethod +- def sequence_schema(cls, sequence_name): +- transaction = Transaction() +- cursor = transaction.connection.cursor() +- for schema in transaction.database.search_path: +- cursor.execute('SELECT 1 ' +- 'FROM information_schema.sequences ' +- 'WHERE sequence_name = %s AND sequence_schema = %s', +- (sequence_name, schema)) +- if cursor.rowcount: +- return schema +- +- @classmethod +- def sequence_exist(cls, sequence_name): +- return bool(cls.sequence_schema(sequence_name)) +- +- @staticmethod +- def sequence_rename(old_name, new_name): +- cursor = Transaction().connection.cursor() +- if (TableHandler.sequence_exist(old_name) +- and not TableHandler.sequence_exist(new_name)): +- cursor.execute('ALTER TABLE "%s" RENAME TO "%s"' +- % (old_name, new_name)) +- + def column_exist(self, column_name): + return column_name in self._columns + +diff --git a/trytond/backend/table.py b/trytond/backend/table.py +index 887d415af80a3f69061808797b70621f73c0108d..853c51918e0b918f0aa9ad392eac7d22697990dd 100644 +--- a/trytond/backend/table.py ++++ b/trytond/backend/table.py +@@ -46,26 +46,6 @@ class TableHandlerInterface(object): + ''' + raise NotImplementedError + +- @staticmethod +- def sequence_exist(sequence_name): +- ''' +- Sequence exist +- +- :param sequence_name: the sequence name +- :return: a boolean +- ''' +- raise NotImplementedError +- +- @staticmethod +- def sequence_rename(old_name, new_name): +- ''' +- Rename sequence +- +- :param old_name: the old sequence name +- :param new_name: the new sequence name +- ''' +- raise NotImplementedError +- + def column_exist(self, column_name): + ''' + Column exist +diff --git a/trytond/ir/sequence.py b/trytond/ir/sequence.py +index 21bc3c12ee62990ce91fe675520582e3db8c41a8..f6791cf692fb6f9161d7a8607c01f638a576309d 100644 +--- a/trytond/ir/sequence.py ++++ b/trytond/ir/sequence.py +@@ -3,7 +3,7 @@ + from string import Template + import time + from itertools import izip +-from sql import Flavor ++from sql import Literal, For + + from ..model import ModelView, ModelSQL, fields, Check + from ..tools import datetime_strftime +@@ -16,7 +16,7 @@ __all__ = [ + 'SequenceType', 'Sequence', 'SequenceStrict', + ] + +-sql_sequence = backend.name() == 'postgresql' ++sql_sequence = backend.get('Database').has_sequence() + + + class SequenceType(ModelSQL, ModelView): +@@ -104,6 +104,7 @@ class Sequence(ModelSQL, ModelView): + def __register__(cls, module_name): + TableHandler = backend.get('TableHandler') + table = TableHandler(cls, module_name) ++ transaction = Transaction() + + # Migration from 2.0 rename number_next into number_next_internal + table.column_rename('number_next', 'number_next_internal') +@@ -116,8 +117,8 @@ class Sequence(ModelSQL, ModelView): + for sequence in sequences: + if sequence.type != 'incremental': + continue +- if not TableHandler.sequence_exist( +- sequence._sql_sequence_name): ++ if not transaction.database.sequence_exist( ++ transaction.connection, sequence._sql_sequence_name): + sequence.create_sql_sequence(sequence.number_next_internal) + + @staticmethod +@@ -159,14 +160,11 @@ class Sequence(ModelSQL, ModelView): + def get_number_next(self, name): + if self.type != 'incremental': + return +- cursor = Transaction().connection.cursor() +- sql_name = self._sql_sequence_name ++ ++ transaction = Transaction() + if sql_sequence and not self._strict: +- cursor.execute('SELECT ' +- 'CASE WHEN NOT is_called THEN last_value ' +- 'ELSE last_value + increment_by ' +- 'END FROM "%s"' % sql_name) +- return cursor.fetchone()[0] ++ return transaction.database.sequence_next_number( ++ transaction.connection, self._sql_sequence_name) + else: + return self.number_next_internal + +@@ -260,22 +258,22 @@ class Sequence(ModelSQL, ModelView): + + def create_sql_sequence(self, number_next=None): + 'Create the SQL sequence' +- cursor = Transaction().connection.cursor() +- param = Flavor.get().param ++ transaction = Transaction() ++ + if self.type != 'incremental': + return + if number_next is None: + number_next = self.number_next +- cursor.execute('CREATE SEQUENCE "' + self._sql_sequence_name +- + '" INCREMENT BY ' + param + ' START WITH ' + param, +- (self.number_increment, number_next)) ++ if sql_sequence: ++ transaction.database.sequence_create(transaction.connection, ++ self._sql_sequence_name, self.number_increment, number_next) + + def update_sql_sequence(self, number_next=None): + 'Update the SQL sequence' +- TableHandler = backend.get('TableHandler') +- cursor = Transaction().connection.cursor() +- param = Flavor.get().param +- exist = TableHandler.sequence_exist(self._sql_sequence_name) ++ transaction = Transaction() ++ ++ exist = transaction.database.sequence_exist( ++ transaction.connection, self._sql_sequence_name) + if self.type != 'incremental': + if exist: + self.delete_sql_sequence() +@@ -285,17 +283,16 @@ class Sequence(ModelSQL, ModelView): + return + if number_next is None: + number_next = self.number_next +- cursor.execute('ALTER SEQUENCE "' + self._sql_sequence_name +- + '" INCREMENT BY ' + param + ' RESTART WITH ' + param, +- (self.number_increment, number_next)) ++ transaction.database.sequence_update(transaction.connection, ++ self._sql_sequence_name, self.number_increment, number_next) + + def delete_sql_sequence(self): + 'Delete the SQL sequence' +- cursor = Transaction().connection.cursor() ++ transaction = Transaction() + if self.type != 'incremental': + return +- cursor.execute('DROP SEQUENCE "%s"' +- % self._sql_sequence_name) ++ transaction.database.sequence_delete( ++ transaction.connection, self._sql_sequence_name) + + @staticmethod + def _process(string, date=None): +diff --git a/trytond/res/ir.py b/trytond/res/ir.py +index 754a6226a6b5e1d7aca4b5575ed8232f9525eb2a..0265c46e4e6857753613a27d6a44575d003c4718 100644 +--- a/trytond/res/ir.py ++++ b/trytond/res/ir.py +@@ -3,6 +3,7 @@ + from ..model import ModelSQL, fields + from .. import backend + from ..pool import Pool, PoolMeta ++from ..transaction import Transaction + + __all__ = [ + 'UIMenuGroup', 'ActionGroup', 'ModelFieldGroup', 'ModelButtonGroup', +@@ -23,10 +24,11 @@ class UIMenuGroup(ModelSQL): + @classmethod + def __register__(cls, module_name): + TableHandler = backend.get('TableHandler') ++ transaction = Transaction() + # Migration from 1.0 table name change + TableHandler.table_rename('ir_ui_menu_group_rel', cls._table) +- TableHandler.sequence_rename('ir_ui_menu_group_rel_id_seq', +- cls._table + '_id_seq') ++ transaction.database.sequence_rename(transaction.connection, ++ 'ir_ui_menu_group_rel_id_seq', cls._table + '_id_seq') + # Migration from 2.0 menu_id and gid renamed into menu group + table = TableHandler(cls, module_name) + table.column_rename('menu_id', 'menu') +@@ -64,10 +66,11 @@ class ActionGroup(ModelSQL): + @classmethod + def __register__(cls, module_name): + TableHandler = backend.get('TableHandler') ++ transaction = Transaction() + # Migration from 1.0 table name change + TableHandler.table_rename('ir_action_group_rel', cls._table) +- TableHandler.sequence_rename('ir_action_group_rel_id_seq', +- cls._table + '_id_seq') ++ transaction.database.sequence_rename(transaction.connection, ++ 'ir_action_group_rel_id_seq', cls._table + '_id_seq') + # Migration from 2.0 action_id and gid renamed into action and group + table = TableHandler(cls, module_name) + table.column_rename('action_id', 'action') +@@ -118,10 +121,12 @@ class ModelFieldGroup(ModelSQL): + @classmethod + def __register__(cls, module_name): + TableHandler = backend.get('TableHandler') ++ database = Transaction().database ++ transaction = Transaction() + # Migration from 1.0 table name change + TableHandler.table_rename('ir_model_field_group_rel', cls._table) +- TableHandler.sequence_rename('ir_model_field_group_rel_id_seq', +- cls._table + '_id_seq') ++ transaction.database.sequence_rename(transaction.connection, ++ 'ir_model_field_group_rel_id_seq', cls._table + '_id_seq') + table = TableHandler(cls, module_name) + # Migration from 2.6: field_id and group_id renamed to field and group + table.column_rename('field_id', 'field') +@@ -176,10 +181,11 @@ class RuleGroupGroup(ModelSQL): + @classmethod + def __register__(cls, module_name): + TableHandler = backend.get('TableHandler') ++ transaction = Transaction() + # Migration from 1.0 table name change + TableHandler.table_rename('group_rule_group_rel', cls._table) +- TableHandler.sequence_rename('group_rule_group_rel_id_seq', +- cls._table + '_id_seq') ++ transaction.database.sequence_rename(transaction.connection, ++ 'group_rule_group_rel_id_seq', cls._table + '_id_seq') + # Migration from 2.0 rule_group_id and group_id renamed into rule_group + # and group + table = TableHandler(cls, module_name) +@@ -199,10 +205,11 @@ class RuleGroupUser(ModelSQL): + @classmethod + def __register__(cls, module_name): + TableHandler = backend.get('TableHandler') ++ transaction = Transaction() + # Migration from 1.0 table name change + TableHandler.table_rename('user_rule_group_rel', cls._table) +- TableHandler.sequence_rename('user_rule_group_rel_id_seq', +- cls._table + '_id_seq') ++ transaction.database.sequence_rename(transaction.connection, ++ 'user_rule_group_rel_id_seq', cls._table + '_id_seq') + # Migration from 2.0 rule_group_id and user_id renamed into rule_group + # and user + table = TableHandler(cls, module_name) +diff --git a/trytond/res/user.py b/trytond/res/user.py +index ac539065b01127b77540c4f3dbdca9b3383d8978..dd13df43b05f9f243d9e1e29573252c0640b7122 100644 +--- a/trytond/res/user.py ++++ b/trytond/res/user.py +@@ -645,10 +645,12 @@ class UserGroup(ModelSQL): + @classmethod + def __register__(cls, module_name): + TableHandler = backend.get('TableHandler') ++ transaction = Transaction() ++ + # Migration from 1.0 table name change + TableHandler.table_rename('res_group_user_rel', cls._table) +- TableHandler.sequence_rename('res_group_user_rel_id_seq', +- cls._table + '_id_seq') ++ transaction.database.sequence_rename(transaction.connection, ++ 'res_group_user_rel_id_seq', cls._table + '_id_seq') + # Migration from 2.0 uid and gid rename into user and group + table = TableHandler(cls, module_name) + table.column_rename('uid', 'user')