trytond/trytond42_psql10.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')