a5183ccfa6
- trytond42_psql10.diff applied for postgres10 compatibility Kudos to mbehrle@m9s.biz! OBS-URL: https://build.opensuse.org/request/show/624098 OBS-URL: https://build.opensuse.org/package/show/Application:ERP:GNUHealth:Factory/trytond?expand=0&rev=28
505 lines
20 KiB
Diff
505 lines
20 KiB
Diff
# Copyright (c) 2018 Mathias Behrle <mbehrle@m9s.biz>
|
|
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')
|